Merge "Creates a vendor APEX for the power and power.stats AIDL HALs."
diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.cpp b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
index d2ba339..8c92cbd 100644
--- a/audio/core/all-versions/vts/functional/7.0/Generators.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
@@ -102,6 +102,9 @@
if (mixPort.getRole() != xsd::Role::source) continue; // not an output profile
auto [flags, isOffload] = generateOutFlags(mixPort);
for (const auto& profile : mixPort.getProfile()) {
+ if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+ !profile.hasChannelMasks())
+ continue;
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (auto& config : configs) {
@@ -231,6 +234,9 @@
std::back_inserter(flags), [](auto flag) { return toString(flag); });
}
for (const auto& profile : mixPort.getProfile()) {
+ if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+ !profile.hasChannelMasks())
+ continue;
auto configs = combineAudioConfig(profile.getChannelMasks(),
profile.getSamplingRates(), profile.getFormat());
for (const auto& config : configs) {
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index e446a7f..c576060 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -239,6 +239,7 @@
data: [
"tests/apm_config_no_vx_7_0.xml",
"tests/apm_config_with_vx_7_0.xml",
+ "tests/apm_config_b_204314749_7_0.xml",
],
test_config: "tests/HalAudioV7_0GeneratorTest.xml",
}
diff --git a/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml b/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml
index 2e79455..3dc5b33 100644
--- a/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml
+++ b/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml
@@ -24,6 +24,7 @@
<option name="cleanup" value="true" />
<option name="push" value="apm_config_no_vx_7_0.xml->/data/local/tmp/apm_config_no_vx.xml" />
<option name="push" value="apm_config_with_vx_7_0.xml->/data/local/tmp/apm_config_with_vx.xml" />
+ <option name="push" value="apm_config_b_204314749_7_0.xml->/data/local/tmp/apm_config_b_204314749_7_0.xml" />
<option name="push" value="HalAudioV7_0GeneratorTest->/data/local/tmp/HalAudioV7_0GeneratorTest" />
</target_preparer>
diff --git a/audio/core/all-versions/vts/functional/tests/apm_config_b_204314749_7_0.xml b/audio/core/all-versions/vts/functional/tests/apm_config_b_204314749_7_0.xml
new file mode 100644
index 0000000..5bdca9a
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/apm_config_b_204314749_7_0.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<audioPolicyConfiguration version="7.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- version section contains a “version” tag in the form “major.minor” e.g. version=”1.0” -->
+
+ <!-- Global configuration Decalaration -->
+ <globalConfiguration speaker_drc_enabled="false"/>
+
+
+ <!-- Modules section:
+ There is one section per audio HW module present on the platform.
+ Each module section will contains two mandatory tags for audio HAL “halVersion” and “name”.
+ The module names are the same as in current .conf file:
+ “primary”, “A2DP”, “remote_submix”, “USB”
+ Each module will contain the following sections:
+ “devicePorts”: a list of device descriptors for all input and output devices accessible via this
+ module.
+ This contains both permanently attached devices and removable devices.
+ “mixPorts”: listing all output and input streams exposed by the audio HAL
+ “routes”: list of possible connections between input and output devices or between stream and
+ devices.
+ "route": is defined by an attribute:
+ -"type": <mux|mix> means all sources are mutual exclusive (mux) or can be mixed (mix)
+ -"sink": the sink involved in this route
+ -"sources": all the sources than can be connected to the sink via vis route
+ “attachedDevices”: permanently attached devices.
+ The attachedDevices section is a list of devices names. The names correspond to device names
+ defined in <devicePorts> section.
+ “defaultOutputDevice”: device to be used by default when no policy rule applies
+ -->
+ <modules>
+ <!-- Primary Audio HAL -->
+ <module name="primary" halVersion="3.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ <item>Built-In Back Mic</item>
+ <item>Echo Reference</item>
+ <item>Tuner</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="tunnel pcm" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_HW_AV_SYNC">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="32000 44100 48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="direct pcm" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="32000 44100 48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="direct output" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name=""/>
+ </mixPort>
+ <mixPort name="tunnel direct output" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_HW_AV_SYNC">
+ <profile name=""/>
+ </mixPort>
+ <mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="tunnel a2dp" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_HW_AV_SYNC">
+ <profile name=""/>
+ </mixPort>
+ <mixPort name="direct a2dp" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name=""/>
+ </mixPort>
+ <mixPort name="echo reference" role="sink">
+ <profile name="echo_reference" format="AUDIO_FORMAT_PCM_32_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="built-in mic" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
+ samplingRates="16000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="ble_in" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <!-- Output devices declaration, i.e. Sink DEVICE PORT -->
+ <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER">
+ <gains>
+ <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-10000"
+ maxValueMB="0"
+ defaultValueMB="-6000"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink">
+ </devicePort>
+ <devicePort tagName="Tuner" role="source" type="AUDIO_DEVICE_IN_TV_TUNER">
+ <gains>
+ <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-10000"
+ maxValueMB="0"
+ defaultValueMB="-6000"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+ <devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+ <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
+ </devicePort>
+ <devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 16000" channelMasks="AUDIO_CHANNEL_OUT_MONO"/>
+ </devicePort>
+ <devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+ <devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+ <devicePort tagName="BT A2DP Speaker" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </devicePort>
+
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source" address="top">
+ <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
+ samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000 16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </devicePort>
+ <devicePort tagName="Echo Reference" type="AUDIO_DEVICE_IN_ECHO_REFERENCE" role="source">
+ <profile name="echo_reference" format="AUDIO_FORMAT_PCM_32_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </devicePort>
+ <devicePort tagName="BLE-In" type="AUDIO_DEVICE_IN_BLUETOOTH_BLE" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </devicePort>
+ </devicePorts>
+ <!-- route declaration, i.e. list all available sources for a given sink -->
+ <routes>
+ <route type="mix" sink="HDMI Out"
+ sources="primary output,tunnel pcm,direct output,Tuner,mmap_no_irq_out,tunnel direct output"/>
+ <route type="mix" sink="Speaker"
+ sources="primary output,tunnel pcm,direct pcm,BT SCO Headset Mic,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="BT SCO"
+ sources="primary output,BT SCO Headset Mic,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="BT SCO Headset"
+ sources="primary output,BT SCO Headset Mic,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="Wired Headset"
+ sources="primary output,tunnel pcm,BT SCO Headset Mic,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="Wired Headphones"
+ sources="primary output,tunnel pcm,BT SCO Headset Mic,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic,Tuner"/>
+ <route type="mix" sink="BT A2DP Out"
+ sources="primary output,tunnel a2dp,direct a2dp,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="BT A2DP Headphones"
+ sources="primary output,tunnel a2dp,direct a2dp,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="BT A2DP Speaker"
+ sources="primary output,tunnel a2dp,direct a2dp,Tuner,mmap_no_irq_out"/>
+ <route type="mix" sink="echo reference"
+ sources="Echo Reference"/>
+ <route type="mix" sink="built-in mic"
+ sources="Built-In Mic"/>
+ <route type="mix" sink="ble_in"
+ sources="BLE-In"/>
+ </routes>
+
+ </module>
+
+ <!-- A2dp Audio HAL -->
+ <!-- <xi:include href="a2dp_audio_policy_configuration.xml"/> -->
+
+ <!-- Usb Audio HAL -->
+ <!-- <xi:include href="usb_audio_policy_configuration.xml"/> -->
+
+ <!-- Remote Submix Audio HAL -->
+ <!-- <xi:include href="r_submix_audio_policy_configuration.xml"/> -->
+
+ <!-- Hearing aid Audio HAL -->
+ <!-- <xi:include href="hearing_aid_audio_policy_configuration.xml"/> -->
+
+ <!-- MSD Audio HAL (optional) -->
+ <!-- <xi:include href="msd_audio_policy_configuration.xml"/> -->
+
+ </modules>
+ <!-- End of Modules section -->
+
+ <!-- Volume section -->
+
+ <!-- <xi:include href="audio_policy_volumes.xml"/> -->
+ <!-- <xi:include href="default_volume_tables.xml"/> -->
+
+ <!-- End of Volume section -->
+
+ <!-- Surround Sound configuration -->
+
+ <surroundSound>
+ <!-- Each of the listed formats gets an entry in Surround Settings dialog on TV devices.
+ There must be a corresponding Java ENCODING_... constant defined in AudioFormat.java,
+ and a display name defined in AudioFormat.toDisplayName. For the formats that don't
+ need a dedicated Surrond Settings dialog entry, a subformats list has to be used. -->
+ <formats>
+ <format name="AUDIO_FORMAT_AC3" />
+ <format name="AUDIO_FORMAT_E_AC3" />
+ <format name="AUDIO_FORMAT_E_AC3_JOC" />
+ <format name="AUDIO_FORMAT_DTS" />
+ </formats>
+ </surroundSound>
+
+ <!-- End of Surround Sound configuration -->
+
+</audioPolicyConfiguration>
diff --git a/audio/core/all-versions/vts/functional/tests/generators_tests.cpp b/audio/core/all-versions/vts/functional/tests/generators_tests.cpp
index 583ff01..7caa712 100644
--- a/audio/core/all-versions/vts/functional/tests/generators_tests.cpp
+++ b/audio/core/all-versions/vts/functional/tests/generators_tests.cpp
@@ -128,5 +128,11 @@
}
// Target file names are the same for all versions, see 'HalAudioVx_0GeneratorTest.xml' test configs
+// clang-format off
INSTANTIATE_TEST_SUITE_P(Generators, GeneratorsTest,
- ::testing::Values("apm_config_no_vx.xml", "apm_config_with_vx.xml"));
+ ::testing::Values("apm_config_no_vx.xml", "apm_config_with_vx.xml"
+#if MAJOR_VERSION == 7
+ , "apm_config_b_204314749_7_0.xml"
+#endif
+ ));
+// clang-format on
diff --git a/automotive/vehicle/2.0/utils/UserHalHelper.cpp b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
index abf59b7..dccdb2b 100644
--- a/automotive/vehicle/2.0/utils/UserHalHelper.cpp
+++ b/automotive/vehicle/2.0/utils/UserHalHelper.cpp
@@ -60,11 +60,22 @@
<< int32Values.size();
}
userInfo->userId = int32Values[startPos];
- auto userFlags = verifyAndCast<UserFlags>(int32Values[startPos + 1]);
- if (!userFlags.ok()) {
- return Error() << "Invalid user flags: " << userFlags.error();
+ int32_t intUserFlags = int32Values[startPos + 1];
+ int32_t expectedUserFlags = 0;
+ for (const auto& v : hidl_enum_range<UserFlags>()) {
+ int32_t intEnumUserFlag = static_cast<int32_t>(v);
+ if ((intUserFlags & intEnumUserFlag) != 0) {
+ expectedUserFlags |= intEnumUserFlag;
+ }
}
- userInfo->flags = *userFlags;
+ if (intUserFlags != expectedUserFlags) {
+ return Error() << "Invalid user flags: " << intUserFlags << ", must be '|' of UserFlags";
+ }
+ // intUserFlags is actually not a valid UserFlags enum, instead, it is a 'bit or' of possible
+ // multiple UserFlags. However, because the HAL interface was defined incorrectly, we have to
+ // cast it to UserFlags here, which is defined behavior because the underlying type for
+ // UserFlags is int32_t and our intUserFlags is within the range of int32_t.
+ userInfo->flags = static_cast<UserFlags>(intUserFlags);
return {};
}
diff --git a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
index 7da87a2..0562a54 100644
--- a/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
+++ b/automotive/vehicle/2.0/utils/tests/UserHalHelper_test.cpp
@@ -54,6 +54,10 @@
constexpr int32_t GUEST_USER = static_cast<int32_t>(UserFlags::GUEST);
constexpr int32_t NONE_USER = static_cast<int32_t>(UserFlags::NONE);
constexpr int32_t SYSTEM_USER = static_cast<int32_t>(UserFlags::SYSTEM);
+constexpr int32_t ADMIN_USER = static_cast<int32_t>(UserFlags::ADMIN);
+constexpr int32_t SYSTEM_ADMIN_USER = static_cast<int32_t>(UserFlags::SYSTEM | UserFlags::ADMIN);
+// 0x1111 is not a valid UserFlags combination.
+constexpr int32_t INVALID_USER_FLAG = 0x1111;
constexpr int32_t USER_ID_ASSOC_KEY_FOB =
static_cast<int32_t>(UserIdentificationAssociationType::KEY_FOB);
@@ -72,7 +76,7 @@
} // namespace
-TEST(UserHalHelperTest, TestToInitialUserInfoRequest) {
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestSystemUser) {
VehiclePropValue propValue{
.prop = INITIAL_USER_INFO,
.value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, SYSTEM_USER,
@@ -92,6 +96,58 @@
EXPECT_THAT(actual.value(), Eq(expected));
}
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestAdminUser) {
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0, ADMIN_USER, 10,
+ NONE_USER}},
+ };
+ InitialUserInfoRequest expected{
+ .requestId = 23,
+ .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+ .usersInfo = {{10, UserFlags::NONE}, 2, {{0, UserFlags::ADMIN}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestUserFlagsBitCombination) {
+ // SYSTEM_ADMIN_USER is two UserFlags combined and is itself not a defined UserFlags enum.
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+ SYSTEM_ADMIN_USER, 10, NONE_USER}},
+ };
+ InitialUserInfoRequest expected{
+ .requestId = 23,
+ .requestType = InitialUserInfoRequestType::FIRST_BOOT_AFTER_OTA,
+ .usersInfo = {{10, UserFlags::NONE},
+ 2,
+ {{0, static_cast<UserFlags>(SYSTEM_ADMIN_USER)}, {10, UserFlags::NONE}}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ ASSERT_TRUE(actual.ok()) << actual.error().message();
+ EXPECT_THAT(actual.value(), Eq(expected));
+}
+
+TEST(UserHalHelperTest, TestToInitialUserInfoRequestUserInvalidUserFlag) {
+ // 0x1111 is not a valid UserFlags flag combination.
+ VehiclePropValue propValue{
+ .prop = INITIAL_USER_INFO,
+ .value = {.int32Values = {23, FIRST_BOOT_AFTER_OTA, 10, NONE_USER, 2, 0,
+ INVALID_USER_FLAG, 10, NONE_USER}},
+ };
+
+ auto actual = toInitialUserInfoRequest(propValue);
+
+ EXPECT_FALSE(actual.ok()) << "No error returned on invalid user flags";
+}
+
TEST(UserHalHelperTest, TestFailsToInitialUserInfoRequestWithMismatchingPropType) {
VehiclePropValue propValue{
.prop = INT32_MAX,
diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc
index 43abbe4..33238da 100644
--- a/bluetooth/1.0/default/h4_protocol.cc
+++ b/bluetooth/1.0/default/h4_protocol.cc
@@ -30,21 +30,52 @@
namespace hci {
size_t H4Protocol::Send(uint8_t type, const uint8_t* data, size_t length) {
- struct iovec iov[] = {{&type, sizeof(type)},
- {const_cast<uint8_t*>(data), length}};
- ssize_t ret = 0;
- do {
- ret =
- TEMP_FAILURE_RETRY(writev(uart_fd_, iov, sizeof(iov) / sizeof(iov[0])));
- } while (-1 == ret && EAGAIN == errno);
-
- if (ret == -1) {
- ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
- } else if (ret < static_cast<ssize_t>(length + 1)) {
- ALOGE("%s: %d / %d bytes written - something went wrong...", __func__,
- static_cast<int>(ret), static_cast<int>(length + 1));
+ struct iovec iov_array[] = {{&type, sizeof(type)},
+ {const_cast<uint8_t*>(data), length}};
+ struct iovec* iov = iov_array;
+ int iovcnt = sizeof(iov_array) / sizeof(iov_array[0]);
+ size_t total_bytes = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ total_bytes += iov_array[i].iov_len;
}
- return ret;
+ size_t bytes_written = 0;
+ size_t remaining_bytes = total_bytes;
+
+ while (remaining_bytes > 0) {
+ ssize_t ret = TEMP_FAILURE_RETRY(writev(uart_fd_, iov, iovcnt));
+ if (ret == -1) {
+ if (errno == EAGAIN) continue;
+ ALOGE("%s error writing to UART (%s)", __func__, strerror(errno));
+ break;
+ } else if (ret == 0) {
+ // Nothing written
+ ALOGE("%s zero bytes written - something went wrong...", __func__);
+ break;
+ } else if (ret == remaining_bytes) {
+ // Everything written
+ bytes_written += ret;
+ break;
+ }
+
+ bytes_written += ret;
+ remaining_bytes -= ret;
+ ALOGW("%s: %d/%d bytes written - retrying remaining %d bytes", __func__,
+ static_cast<int>(bytes_written), static_cast<int>(total_bytes),
+ static_cast<int>(remaining_bytes));
+
+ // Remove iovs which are written from the list
+ while (ret >= iov->iov_len) {
+ ret -= iov->iov_len;
+ ++iov;
+ --iovcnt;
+ }
+ // Adjust the iov to point to the remaining data which needs to be written
+ if (ret) {
+ iov->iov_base = static_cast<uint8_t*>(iov->iov_base) + ret;
+ iov->iov_len -= ret;
+ }
+ }
+ return bytes_written;
}
void H4Protocol::OnPacketReady() {
diff --git a/bluetooth/audio/2.2/Android.bp b/bluetooth/audio/2.2/Android.bp
new file mode 100644
index 0000000..6449c08
--- /dev/null
+++ b/bluetooth/audio/2.2/Android.bp
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+ name: "android.hardware.bluetooth.audio@2.2",
+ root: "android.hardware",
+ srcs: [
+ "types.hal",
+ "IBluetoothAudioProvider.hal",
+ "IBluetoothAudioProvidersFactory.hal",
+ ],
+ interfaces: [
+ "android.hardware.audio.common@5.0",
+ "android.hardware.bluetooth.audio@2.0",
+ "android.hardware.bluetooth.audio@2.1",
+ "android.hidl.base@1.0",
+ "android.hidl.safe_union@1.0",
+ ],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ ],
+ gen_java: false,
+}
diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvider.hal b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
new file mode 100644
index 0000000..ad8c839
--- /dev/null
+++ b/bluetooth/audio/2.2/IBluetoothAudioProvider.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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.bluetooth.audio@2.2;
+
+import @2.1::IBluetoothAudioProvider;
+import @2.0::IBluetoothAudioPort;
+import @2.0::Status;
+
+/**
+ * HAL interface from the Bluetooth stack to the Audio HAL
+ *
+ * The Bluetooth stack calls methods in this interface to start and end audio
+ * sessions and sends callback events to the Audio HAL.
+ *
+ * Note: For HIDL APIs with a "generates" statement, the callback parameter used
+ * for return value must be invoked synchronously before the API call returns.
+ */
+interface IBluetoothAudioProvider extends @2.1::IBluetoothAudioProvider {
+
+ /**
+ * This method indicates that the Bluetooth stack is ready to stream audio.
+ * It registers an instance of IBluetoothAudioPort with and provides the
+ * current negotiated codec to the Audio HAL. After this method is called,
+ * the Audio HAL can invoke IBluetoothAudioPort.startStream().
+ *
+ * Note: endSession() must be called to unregister this IBluetoothAudioPort
+ *
+ * @param hostIf An instance of IBluetoothAudioPort for stream control
+ * @param audioConfig The audio configuration negotiated with the remote
+ * device. The PCM parameters are set if software based encoding,
+ * otherwise the correct codec configuration is used for hardware
+ * encoding.
+ *
+ * @return status One of the following
+ * SUCCESS if this IBluetoothAudioPort was successfully registered with
+ * the Audio HAL
+ * UNSUPPORTED_CODEC_CONFIGURATION if the Audio HAL cannot register this
+ * IBluetoothAudioPort with the given codec configuration
+ * FAILURE if the Audio HAL cannot register this IBluetoothAudioPort for
+ * any other reason
+ * @return dataMQ The fast message queue for audio data from/to this
+ * provider. Audio data will be in PCM format as specified by the
+ * audioConfig.pcmConfig parameter. Invalid if streaming is offloaded
+ * from/to hardware or on failure.
+ */
+ startSession_2_2(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig)
+ generates (Status status, fmq_sync<uint8_t> dataMQ);
+};
diff --git a/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal b/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal
new file mode 100644
index 0000000..eeff4de
--- /dev/null
+++ b/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.hal
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.bluetooth.audio@2.2;
+
+import @2.1::IBluetoothAudioProvidersFactory;
+
+/**
+ * This factory allows a HAL implementation to be split into multiple
+ * independent providers.
+ *
+ * When the Bluetooth stack is ready to create an audio session, it must first
+ * obtain the IBluetoothAudioProvider for that session type by calling
+ * openProvider().
+ *
+ * Note: For HIDL APIs with a "generates" statement, the callback parameter used
+ * for return value must be invoked synchronously before the API call returns.
+ */
+interface IBluetoothAudioProvidersFactory extends @2.1::IBluetoothAudioProvidersFactory {
+};
diff --git a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp
new file mode 100644
index 0000000..126bc9e
--- /dev/null
+++ b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpOffload"
+
+#include "A2dpOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
+ : BluetoothAudioProvider() {
+ session_type_ = V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
+
+bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+ return isValid(static_cast<V2_1::SessionType>(sessionType));
+}
+
+bool A2dpOffloadAudioProvider::isValid(const V2_1::SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> A2dpOffloadAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::codecConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid(
+ session_type_, audioConfig.codecConfig())) {
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpOffloadAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ BluetoothAudioSessionReport_2_2::OnSessionStarted(session_type_, stack_iface_,
+ nullptr, audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h
new file mode 100644
index 0000000..7ccdedc
--- /dev/null
+++ b/bluetooth/audio/2.2/default/A2dpOffloadAudioProvider.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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 "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpOffloadAudioProvider();
+
+ bool isValid(const V2_1::SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp
new file mode 100644
index 0000000..0d918e1
--- /dev/null
+++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpSoftware"
+
+#include "A2dpSoftwareAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+// Here the buffer size is based on SBC
+static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
+// SBC is 128, and here we choose the LCM of 16, 24, and 32
+static constexpr uint32_t kPcmFrameCount = 96;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+// The max counts by 1 tick (20ms) for SBC is about 7. Since using 96 for the
+// PCM counts, here we just choose a greater number
+static constexpr uint32_t kRtpFrameCount = 10;
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 2; // double buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
+ : BluetoothAudioProvider(), mDataMQ(nullptr) {
+ LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+ << " byte(s)";
+ std::unique_ptr<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = V2_1::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
+ } else {
+ ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+ }
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+ return isValid(static_cast<V2_1::SessionType>(sessionType));
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const V2_1::SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> A2dpSoftwareAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+ audioConfig.pcmConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpSoftwareAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport_2_2::OnSessionStarted(
+ session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+ } else {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h
new file mode 100644
index 0000000..3d4f0cc
--- /dev/null
+++ b/bluetooth/audio/2.2/default/A2dpSoftwareAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
+ public:
+ A2dpSoftwareAudioProvider();
+
+ bool isValid(const V2_1::SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/Android.bp b/bluetooth/audio/2.2/default/Android.bp
new file mode 100644
index 0000000..7a5ae75
--- /dev/null
+++ b/bluetooth/audio/2.2/default/Android.bp
@@ -0,0 +1,37 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+ name: "android.hardware.bluetooth.audio@2.2-impl",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "BluetoothAudioProvidersFactory.cpp",
+ "BluetoothAudioProvider.cpp",
+ "A2dpOffloadAudioProvider.cpp",
+ "A2dpSoftwareAudioProvider.cpp",
+ "HearingAidAudioProvider.cpp",
+ "LeAudioAudioProvider.cpp",
+ "LeAudioOffloadAudioProvider.cpp",
+ ],
+ header_libs: ["libhardware_headers"],
+ shared_libs: [
+ "android.hardware.bluetooth.audio@2.0",
+ "android.hardware.bluetooth.audio@2.1",
+ "android.hardware.bluetooth.audio@2.2",
+ "libbase",
+ "libbluetooth_audio_session",
+ "libcutils",
+ "libfmq",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
new file mode 100644
index 0000000..3655bc0
--- /dev/null
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderStub"
+
+#include "BluetoothAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+void BluetoothAudioDeathRecipient::serviceDied(
+ uint64_t cookie __unused,
+ const wp<::android::hidl::base::V1_0::IBase>& who __unused) {
+ LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__
+ << " - BluetoothAudio Service died";
+ provider_->endSession();
+}
+
+BluetoothAudioProvider::BluetoothAudioProvider()
+ : death_recipient_(new BluetoothAudioDeathRecipient(this)),
+ session_type_(V2_1::SessionType::UNKNOWN),
+ audio_config_({}) {}
+
+Return<void> BluetoothAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ AudioConfiguration audioConfig_2_2;
+
+ if (audioConfig.getDiscriminator() ==
+ V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) {
+ audioConfig_2_2.pcmConfig(
+ {.sampleRate =
+ static_cast<V2_1::SampleRate>(audioConfig.pcmConfig().sampleRate),
+ .channelMode = audioConfig.pcmConfig().channelMode,
+ .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+ .dataIntervalUs = 0});
+ } else {
+ audioConfig_2_2.codecConfig(audioConfig.codecConfig());
+ }
+
+ return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+}
+
+Return<void> BluetoothAudioProvider::startSession_2_1(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ AudioConfiguration audioConfig_2_2;
+ if (audioConfig.getDiscriminator() ==
+ V2_1::AudioConfiguration::hidl_discriminator::leAudioCodecConfig) {
+ audioConfig_2_2.leAudioConfig().mode = LeAudioMode::UNKNOWN;
+ audioConfig_2_2.leAudioConfig().config.unicastConfig() = {
+ .streamMap = {{
+ .streamHandle = 0xFFFF,
+ .audioChannelAllocation =
+ audioConfig.leAudioCodecConfig().audioChannelAllocation,
+ }},
+ .peerDelay = 0,
+ .lc3Config = audioConfig.leAudioCodecConfig().lc3Config};
+ } else if (audioConfig.getDiscriminator() ==
+ V2_1::AudioConfiguration::hidl_discriminator::pcmConfig) {
+ audioConfig_2_2.pcmConfig(audioConfig.pcmConfig());
+ } else {
+ audioConfig_2_2.codecConfig(audioConfig.codecConfig());
+ }
+
+ return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+}
+
+Return<void> BluetoothAudioProvider::startSession_2_2(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ if (hostIf == nullptr) {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ return Void();
+ }
+
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ audio_config_ = audioConfig;
+ stack_iface_ = hostIf;
+ stack_iface_->linkToDeath(death_recipient_, 0);
+
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", AudioConfiguration=[" << toString(audio_config_) << "]";
+
+ onSessionReady(_hidl_cb);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamStarted(
+ BluetoothAudioStatus status) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status);
+
+ /**
+ * Streaming on control path has started,
+ * HAL server should start the streaming on data path.
+ */
+ if (stack_iface_) {
+ BluetoothAudioSessionReport_2_2::ReportControlStatus(session_type_, true,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamSuspended(
+ BluetoothAudioStatus status) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status);
+
+ /**
+ * Streaming on control path has suspend,
+ * HAL server should suspend the streaming on data path.
+ */
+ if (stack_iface_) {
+ BluetoothAudioSessionReport_2_2::ReportControlStatus(session_type_, false,
+ status);
+ } else {
+ LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+ << ", status=" << toString(status) << " has NO session";
+ }
+
+ return Void();
+}
+
+Return<void> BluetoothAudioProvider::endSession() {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+ if (stack_iface_) {
+ BluetoothAudioSessionReport_2_2::OnSessionEnded(session_type_);
+ stack_iface_->unlinkToDeath(death_recipient_);
+ } else {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+ << " has NO session";
+ }
+
+ /**
+ * Clean up the audio platform as remote audio device is no
+ * longer active
+ */
+ stack_iface_ = nullptr;
+ audio_config_ = {};
+
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvider.h b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
new file mode 100644
index 0000000..b7581ba
--- /dev/null
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvider.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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/bluetooth/audio/2.2/IBluetoothAudioProvider.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+
+using BluetoothAudioStatus =
+ ::android::hardware::bluetooth::audio::V2_0::Status;
+
+class BluetoothAudioDeathRecipient;
+
+class BluetoothAudioProvider : public IBluetoothAudioProvider {
+ public:
+ BluetoothAudioProvider();
+ ~BluetoothAudioProvider() = default;
+
+ virtual bool isValid(const V2_1::SessionType& sessionType) = 0;
+ virtual bool isValid(const V2_0::SessionType& sessionType) = 0;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> startSession_2_1(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> startSession_2_2(const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+ Return<void> streamStarted(BluetoothAudioStatus status) override;
+ Return<void> streamSuspended(BluetoothAudioStatus status) override;
+ Return<void> endSession() override;
+
+ protected:
+ sp<BluetoothAudioDeathRecipient> death_recipient_;
+
+ V2_1::SessionType session_type_;
+ AudioConfiguration audio_config_;
+ sp<V2_0::IBluetoothAudioPort> stack_iface_;
+
+ virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
+};
+
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+ BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
+ : provider_(provider) {}
+
+ virtual void serviceDied(
+ uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ sp<BluetoothAudioProvider> provider_;
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp
new file mode 100644
index 0000000..7438c80
--- /dev/null
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProvidersFactory"
+
+#include "BluetoothAudioProvidersFactory.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+
+A2dpSoftwareAudioProvider
+ BluetoothAudioProvidersFactory::a2dp_software_provider_instance_;
+A2dpOffloadAudioProvider
+ BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_;
+HearingAidAudioProvider
+ BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
+LeAudioOutputAudioProvider
+ BluetoothAudioProvidersFactory::leaudio_output_provider_instance_;
+LeAudioOffloadOutputAudioProvider
+ BluetoothAudioProvidersFactory::leaudio_offload_output_provider_instance_;
+LeAudioInputAudioProvider
+ BluetoothAudioProvidersFactory::leaudio_input_provider_instance_;
+LeAudioOffloadInputAudioProvider
+ BluetoothAudioProvidersFactory::leaudio_offload_input_provider_instance_;
+
+Return<void> BluetoothAudioProvidersFactory::openProvider(
+ const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+ BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+ BluetoothAudioProvider* provider = nullptr;
+
+ switch (sessionType) {
+ case V2_0::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+ provider = &a2dp_software_provider_instance_;
+ break;
+ case V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+ provider = &a2dp_offload_provider_instance_;
+ break;
+ case V2_0::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+ provider = &hearing_aid_provider_instance_;
+ break;
+ default:
+ status = BluetoothAudioStatus::FAILURE;
+ }
+
+ if (provider == nullptr || !provider->isValid(sessionType)) {
+ provider = nullptr;
+ status = BluetoothAudioStatus::FAILURE;
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+ << ", status=" << toString(status);
+ }
+
+ _hidl_cb(status, provider);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::openProvider_2_1(
+ const V2_1::SessionType sessionType, openProvider_2_1_cb _hidl_cb) {
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+ BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+ BluetoothAudioProvider* provider = nullptr;
+
+ switch (sessionType) {
+ case V2_1::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+ provider = &a2dp_software_provider_instance_;
+ break;
+ case V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+ provider = &a2dp_offload_provider_instance_;
+ break;
+ case V2_1::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+ provider = &hearing_aid_provider_instance_;
+ break;
+ case V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
+ provider = &leaudio_output_provider_instance_;
+ break;
+ case V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+ provider = &leaudio_offload_output_provider_instance_;
+ break;
+ case V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH:
+ provider = &leaudio_input_provider_instance_;
+ break;
+ case V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
+ provider = &leaudio_offload_input_provider_instance_;
+ break;
+ default:
+ status = BluetoothAudioStatus::FAILURE;
+ }
+
+ if (provider == nullptr || !provider->isValid(sessionType)) {
+ provider = nullptr;
+ status = BluetoothAudioStatus::FAILURE;
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+ << ", status=" << toString(status);
+ }
+
+ _hidl_cb(status, provider);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
+ const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
+ hidl_vec<V2_0::AudioCapabilities> audio_capabilities =
+ hidl_vec<V2_0::AudioCapabilities>(0);
+ if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ std::vector<CodecCapabilities> db_codec_capabilities =
+ android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+ if (db_codec_capabilities.size()) {
+ audio_capabilities.resize(db_codec_capabilities.size());
+ for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+ audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+ }
+ }
+ } else if (sessionType != V2_0::SessionType::UNKNOWN) {
+ std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
+ db_pcm_capabilities =
+ android::bluetooth::audio::GetSoftwarePcmCapabilities();
+ if (db_pcm_capabilities.size() == 1) {
+ audio_capabilities.resize(1);
+ audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+ }
+ }
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+ << " supports " << audio_capabilities.size() << " codecs";
+ _hidl_cb(audio_capabilities);
+ return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities_2_1(
+ const V2_1::SessionType sessionType,
+ getProviderCapabilities_2_1_cb _hidl_cb) {
+ hidl_vec<V2_1::AudioCapabilities> audio_capabilities =
+ hidl_vec<V2_1::AudioCapabilities>(0);
+ if (sessionType == V2_1::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ std::vector<CodecCapabilities> db_codec_capabilities =
+ android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+ if (db_codec_capabilities.size()) {
+ audio_capabilities.resize(db_codec_capabilities.size());
+ for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+ audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+ }
+ }
+ } else if (sessionType != V2_1::SessionType::UNKNOWN) {
+ std::vector<V2_1::PcmParameters> db_pcm_capabilities =
+ android::bluetooth::audio::GetSoftwarePcmCapabilities_2_1();
+ if (db_pcm_capabilities.size() == 1) {
+ audio_capabilities.resize(1);
+ audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+ }
+ }
+ LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+ << " supports " << audio_capabilities.size() << " codecs";
+ _hidl_cb(audio_capabilities);
+ return Void();
+}
+
+IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory(
+ const char* /* name */) {
+ return new BluetoothAudioProvidersFactory();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h
new file mode 100644
index 0000000..8db330b
--- /dev/null
+++ b/bluetooth/audio/2.2/default/BluetoothAudioProvidersFactory.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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/bluetooth/audio/2.2/IBluetoothAudioProvidersFactory.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioProvider.h"
+#include "HearingAidAudioProvider.h"
+#include "LeAudioAudioProvider.h"
+#include "LeAudioOffloadAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
+ public:
+ BluetoothAudioProvidersFactory() {}
+
+ Return<void> openProvider(const V2_0::SessionType sessionType,
+ openProvider_cb _hidl_cb) override;
+
+ Return<void> getProviderCapabilities(
+ const V2_0::SessionType sessionType,
+ getProviderCapabilities_cb _hidl_cb) override;
+
+ Return<void> openProvider_2_1(const V2_1::SessionType sessionType,
+ openProvider_2_1_cb _hidl_cb) override;
+
+ Return<void> getProviderCapabilities_2_1(
+ const V2_1::SessionType sessionType,
+ getProviderCapabilities_2_1_cb _hidl_cb) override;
+
+ private:
+ static A2dpSoftwareAudioProvider a2dp_software_provider_instance_;
+ static A2dpOffloadAudioProvider a2dp_offload_provider_instance_;
+ static HearingAidAudioProvider hearing_aid_provider_instance_;
+ static LeAudioOutputAudioProvider leaudio_output_provider_instance_;
+ static LeAudioInputAudioProvider leaudio_input_provider_instance_;
+ static LeAudioOffloadOutputAudioProvider
+ leaudio_offload_output_provider_instance_;
+ static LeAudioOffloadInputAudioProvider
+ leaudio_offload_input_provider_instance_;
+};
+
+extern "C" IBluetoothAudioProvidersFactory*
+HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp
new file mode 100644
index 0000000..c79b910
--- /dev/null
+++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderHearingAid"
+
+#include "HearingAidAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+static constexpr uint32_t kPcmFrameSize = 4; // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7; // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 1; // single buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+HearingAidAudioProvider::HearingAidAudioProvider()
+ : BluetoothAudioProvider(), mDataMQ(nullptr) {
+ LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+ << " byte(s)";
+ std::unique_ptr<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ session_type_ = V2_1::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
+ } else {
+ ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+ }
+}
+
+bool HearingAidAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+ return isValid(static_cast<V2_1::SessionType>(sessionType));
+}
+
+bool HearingAidAudioProvider::isValid(const V2_1::SessionType& sessionType) {
+ return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> HearingAidAudioProvider::startSession(
+ const sp<IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+ audioConfig.pcmConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport_2_2::OnSessionStarted(
+ session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+ } else {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/HearingAidAudioProvider.h b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h
new file mode 100644
index 0000000..426c443
--- /dev/null
+++ b/bluetooth/audio/2.2/default/HearingAidAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class HearingAidAudioProvider : public BluetoothAudioProvider {
+ public:
+ HearingAidAudioProvider();
+
+ bool isValid(const V2_1::SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+ const V2_0::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ // audio data queue for software encoding
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp
new file mode 100644
index 0000000..af6ec99
--- /dev/null
+++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderLeAudio"
+
+#include "LeAudioAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+static constexpr uint32_t kBufferOutCount = 2; // two frame buffer
+static constexpr uint32_t kBufferInCount = 2; // two frame buffer
+
+LeAudioOutputAudioProvider::LeAudioOutputAudioProvider()
+ : LeAudioAudioProvider() {
+ session_type_ = V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
+}
+
+LeAudioInputAudioProvider::LeAudioInputAudioProvider()
+ : LeAudioAudioProvider() {
+ session_type_ = V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH;
+}
+
+LeAudioAudioProvider::LeAudioAudioProvider()
+ : BluetoothAudioProvider(), mDataMQ(nullptr) {}
+
+bool LeAudioAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+ LOG(ERROR) << __func__ << ", invalid session type for Le Audio provider: "
+ << toString(sessionType);
+
+ return false;
+}
+
+bool LeAudioAudioProvider::isValid(const V2_1::SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> LeAudioAudioProvider::startSession_2_1(
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ if (audioConfig.getDiscriminator() !=
+ V2_1::AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ AudioConfiguration audioConfig_2_2;
+ audioConfig_2_2.pcmConfig(
+ {.sampleRate =
+ static_cast<V2_1::SampleRate>(audioConfig.pcmConfig().sampleRate),
+ .channelMode = audioConfig.pcmConfig().channelMode,
+ .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+ .dataIntervalUs = 0});
+
+ return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+}
+
+Return<void> LeAudioAudioProvider::startSession_2_2(
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::pcmConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid_2_1(
+ audioConfig.pcmConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ uint32_t kDataMqSize = 0;
+ switch (audioConfig.pcmConfig().sampleRate) {
+ case SampleRate::RATE_8000:
+ kDataMqSize = 8000;
+ break;
+ case SampleRate::RATE_16000:
+ kDataMqSize = 16000;
+ break;
+ case SampleRate::RATE_24000:
+ kDataMqSize = 24000;
+ break;
+ case SampleRate::RATE_32000:
+ kDataMqSize = 32000;
+ break;
+ case SampleRate::RATE_44100:
+ kDataMqSize = 44100;
+ break;
+ case SampleRate::RATE_48000:
+ kDataMqSize = 48000;
+ break;
+ default:
+ LOG(WARNING) << __func__ << " - Unsupported sampling frequency="
+ << toString(audioConfig.pcmConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ /* Number of samples per millisecond */
+ kDataMqSize = ceil(kDataMqSize / 1000);
+
+ switch (audioConfig.pcmConfig().channelMode) {
+ case ChannelMode::MONO:
+ break;
+ case ChannelMode::STEREO:
+ kDataMqSize *= 2;
+ break;
+ default:
+ /* This should never happen it would be caught while validating
+ * parameters.
+ */
+ break;
+ }
+
+ switch (audioConfig.pcmConfig().bitsPerSample) {
+ case BitsPerSample::BITS_16:
+ kDataMqSize *= 2;
+ break;
+ case BitsPerSample::BITS_24:
+ kDataMqSize *= 3;
+ break;
+ case BitsPerSample::BITS_32:
+ kDataMqSize *= 4;
+ break;
+ default:
+ /* This should never happen it would be caught while validating
+ * parameters.
+ */
+ break;
+ }
+
+ if (session_type_ == V2_1::SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH)
+ kDataMqSize *= kBufferOutCount;
+ else if (session_type_ ==
+ V2_1::SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH)
+ kDataMqSize *= kBufferInCount;
+ else
+ LOG(WARNING) << __func__ << ", default single buffer used";
+
+ kDataMqSize *= audioConfig.pcmConfig().dataIntervalUs / 1000;
+
+ LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+ << " byte(s)";
+
+ std::unique_ptr<DataMQ> tempDataMQ(
+ new DataMQ(kDataMqSize, /* EventFlag */ true));
+ if (tempDataMQ && tempDataMQ->isValid()) {
+ mDataMQ = std::move(tempDataMQ);
+ } else {
+ ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+ ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession_2_2(hostIf, audioConfig,
+ _hidl_cb);
+}
+
+Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+ if (mDataMQ && mDataMQ->isValid()) {
+ BluetoothAudioSessionReport_2_2::OnSessionStarted(
+ session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+ } else {
+ _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+ }
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/LeAudioAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h
new file mode 100644
index 0000000..40c26e0
--- /dev/null
+++ b/bluetooth/audio/2.2/default/LeAudioAudioProvider.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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/bluetooth/audio/2.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class LeAudioAudioProvider : public BluetoothAudioProvider {
+ public:
+ LeAudioAudioProvider();
+
+ bool isValid(const V2_1::SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ Return<void> startSession_2_2(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ /** queue for software encodec/decoded audio data */
+ std::unique_ptr<DataMQ> mDataMQ;
+
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOutputAudioProvider : public LeAudioAudioProvider {
+ public:
+ LeAudioOutputAudioProvider();
+};
+
+class LeAudioInputAudioProvider : public LeAudioAudioProvider {
+ public:
+ LeAudioInputAudioProvider();
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp
new file mode 100644
index 0000000..7b70654
--- /dev/null
+++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderLeAudioOffload"
+
+#include "LeAudioOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_2.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_2.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_2;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
+ : LeAudioOffloadAudioProvider() {
+ session_type_ =
+ V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
+}
+
+LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider()
+ : LeAudioOffloadAudioProvider() {
+ session_type_ =
+ V2_1::SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
+}
+
+LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider()
+ : BluetoothAudioProvider() {}
+
+bool LeAudioOffloadAudioProvider::isValid(
+ const V2_0::SessionType& sessionType) {
+ LOG(ERROR) << __func__
+ << ", invalid session type for Offloaded Le Audio provider: "
+ << toString(sessionType);
+
+ return false;
+}
+
+bool LeAudioOffloadAudioProvider::isValid(
+ const V2_1::SessionType& sessionType) {
+ return (sessionType == session_type_);
+}
+
+Return<void> LeAudioOffloadAudioProvider::startSession_2_1(
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ if (audioConfig.getDiscriminator() !=
+ V2_1::AudioConfiguration::hidl_discriminator::leAudioCodecConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ AudioConfiguration audioConfig_2_2;
+ audioConfig_2_2.leAudioConfig().mode = LeAudioMode::UNKNOWN;
+ audioConfig_2_2.leAudioConfig().config.unicastConfig() = {
+ .streamMap = {{
+ .streamHandle = 0xFFFF,
+ .audioChannelAllocation =
+ audioConfig.leAudioCodecConfig().audioChannelAllocation,
+ }},
+ .peerDelay = 0,
+ .lc3Config = audioConfig.leAudioCodecConfig().lc3Config};
+
+ return startSession_2_2(hostIf, audioConfig_2_2, _hidl_cb);
+}
+
+Return<void> LeAudioOffloadAudioProvider::startSession_2_2(
+ const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+ /**
+ * Initialize the audio platform if audioConfiguration is supported.
+ * Save the IBluetoothAudioPort interface, so that it can be used
+ * later to send stream control commands to the HAL client, based on
+ * interaction with Audio framework.
+ */
+ if (audioConfig.getDiscriminator() !=
+ AudioConfiguration::hidl_discriminator::leAudioConfig) {
+ LOG(WARNING) << __func__
+ << " - Invalid Audio Configuration=" << toString(audioConfig);
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ if (!android::bluetooth::audio::IsOffloadLeAudioConfigurationValid(
+ session_type_, audioConfig.leAudioConfig())) {
+ LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
+ << toString(audioConfig.leAudioConfig());
+ _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+ DataMQ::Descriptor());
+ return Void();
+ }
+
+ return BluetoothAudioProvider::startSession_2_2(hostIf, audioConfig,
+ _hidl_cb);
+}
+
+Return<void> LeAudioOffloadAudioProvider::onSessionReady(
+ startSession_cb _hidl_cb) {
+ BluetoothAudioSessionReport_2_2::OnSessionStarted(session_type_, stack_iface_,
+ nullptr, audio_config_);
+ _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h
new file mode 100644
index 0000000..5620295
--- /dev/null
+++ b/bluetooth/audio/2.2/default/LeAudioOffloadAudioProvider.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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/bluetooth/audio/2.2/types.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_2 {
+namespace implementation {
+
+class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+ LeAudioOffloadAudioProvider();
+
+ bool isValid(const V2_1::SessionType& sessionType) override;
+ bool isValid(const V2_0::SessionType& sessionType) override;
+
+ Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const V2_1::AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ Return<void> startSession_2_2(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+ const AudioConfiguration& audioConfig,
+ startSession_cb _hidl_cb) override;
+
+ private:
+ Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+ LeAudioOffloadOutputAudioProvider();
+};
+
+class LeAudioOffloadInputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+ LeAudioOffloadInputAudioProvider();
+};
+
+} // namespace implementation
+} // namespace V2_2
+} // namespace audio
+} // namespace bluetooth
+} // namespace hardware
+} // namespace android
diff --git a/bluetooth/audio/2.2/types.hal b/bluetooth/audio/2.2/types.hal
new file mode 100644
index 0000000..d5f8a3f
--- /dev/null
+++ b/bluetooth/audio/2.2/types.hal
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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.bluetooth.audio@2.2;
+
+import @2.1::Lc3Parameters;
+import @2.1::PcmParameters;
+import @2.0::CodecConfiguration;
+
+enum LeAudioMode : uint8_t {
+ UNKNOWN = 0x00,
+ UNICAST = 0x01,
+ BROADCAST = 0x02,
+};
+
+struct UnicastStreamMap {
+ /* The connection handle used for a unicast or a broadcast group. */
+ uint16_t streamHandle;
+ /* Audio channel allocation is a bit field, each enabled bit means that given audio direction,
+ * i.e. "left", or "right" is used. Ordering of audio channels comes from the least significant
+ * bit to the most significant bit. */
+ uint32_t audioChannelAllocation;
+};
+
+struct BroadcastStreamMap {
+ /* The connection handle used for a unicast or a broadcast group. */
+ uint16_t streamHandle;
+ /* Audio channel allocation is a bit field, each enabled bit means that given audio direction,
+ * i.e. "left", or "right" is used. Ordering of audio channels comes from the least significant
+ * bit to the most significant bit. */
+ uint32_t audioChannelAllocation;
+ Lc3Parameters lc3Config;
+};
+
+struct UnicastConfig {
+ vec<UnicastStreamMap> streamMap;
+ uint32_t peerDelay;
+ Lc3Parameters lc3Config;
+};
+
+struct BroadcastConfig {
+ vec<BroadcastStreamMap> streamMap;
+};
+
+struct LeAudioConfiguration {
+ /* The mode of the LE audio */
+ LeAudioMode mode;
+ safe_union CodecConfig {
+ UnicastConfig unicastConfig;
+ BroadcastConfig broadcastConfig;
+ } config;
+};
+
+/** Used to configure either a Hardware or Software Encoding session based on session type */
+safe_union AudioConfiguration {
+ PcmParameters pcmConfig;
+ CodecConfiguration codecConfig;
+ LeAudioConfiguration leAudioConfig;
+};
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
index 551bc50..19d2d92 100644
--- a/bluetooth/audio/utils/Android.bp
+++ b/bluetooth/audio/utils/Android.bp
@@ -14,14 +14,17 @@
srcs: [
"session/BluetoothAudioSession.cpp",
"session/BluetoothAudioSession_2_1.cpp",
+ "session/BluetoothAudioSession_2_2.cpp",
"session/BluetoothAudioSupportedCodecsDB.cpp",
"session/BluetoothAudioSupportedCodecsDB_2_1.cpp",
+ "session/BluetoothAudioSupportedCodecsDB_2_2.cpp",
],
export_include_dirs: ["session/"],
header_libs: ["libhardware_headers"],
shared_libs: [
"android.hardware.bluetooth.audio@2.0",
"android.hardware.bluetooth.audio@2.1",
+ "android.hardware.bluetooth.audio@2.2",
"libbase",
"libcutils",
"libfmq",
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.h b/bluetooth/audio/utils/session/BluetoothAudioSession.h
index 83e20ad..3469cc0 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession.h
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.h
@@ -80,6 +80,7 @@
class BluetoothAudioSession {
friend class BluetoothAudioSession_2_1;
+ friend class BluetoothAudioSession_2_2;
private:
// using recursive_mutex to allow hwbinder to re-enter agian.
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h
new file mode 100644
index 0000000..194259a
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_2.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 "BluetoothAudioSession_2_2.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionReport_2_2 {
+ public:
+ // The API reports the Bluetooth stack has started the session, and will
+ // inform registered bluetooth_audio outputs
+ static void OnSessionStarted(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type,
+ const sp<IBluetoothAudioPort> host_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+ audio_config) {
+ std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+ BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
+ }
+ }
+
+ // The API reports the Bluetooth stack has ended the session, and will
+ // inform registered bluetooth_audio outputs
+ static void OnSessionEnded(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type) {
+ std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+ BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->GetAudioSession()->OnSessionEnded();
+ }
+ }
+ // The API reports the Bluetooth stack has replied the result of startStream
+ // or suspendStream, and will inform registered bluetooth_audio outputs
+ static void ReportControlStatus(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type,
+ const bool& start_resp, const BluetoothAudioStatus& status) {
+ std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+ BluetoothAudioSessionInstance_2_2::GetSessionInstance(session_type);
+ if (session_ptr != nullptr) {
+ session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status);
+ }
+ }
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
new file mode 100644
index 0000000..9d9ea41
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BTAudioProviderSession_2_2"
+
+#include "BluetoothAudioSession_2_2.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+using SessionType_2_1 =
+ ::android::hardware::bluetooth::audio::V2_1::SessionType;
+using SessionType_2_0 =
+ ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ BluetoothAudioSession_2_2::invalidSoftwareAudioConfiguration = {};
+::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ BluetoothAudioSession_2_2::invalidOffloadAudioConfiguration = {};
+
+namespace {
+bool is_2_0_session_type(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type) {
+ if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+ session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
+ return true;
+ } else {
+ return false;
+ }
+}
+} // namespace
+
+BluetoothAudioSession_2_2::BluetoothAudioSession_2_2(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type)
+ : audio_session(BluetoothAudioSessionInstance::GetSessionInstance(
+ static_cast<SessionType_2_0>(session_type))) {
+ if (is_2_0_session_type(session_type)) {
+ session_type_2_1_ = (SessionType_2_1::UNKNOWN);
+ } else {
+ session_type_2_1_ = (session_type);
+ }
+}
+
+bool BluetoothAudioSession_2_2::IsSessionReady() {
+ if (session_type_2_1_ !=
+ SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
+ return audio_session->IsSessionReady();
+ }
+
+ std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+ return audio_session->stack_iface_ != nullptr;
+}
+
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSession_2_2::GetAudioSession() {
+ return audio_session;
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+BluetoothAudioSession_2_2::GetAudioConfig() {
+ std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+ if (IsSessionReady()) {
+ // If session is unknown it means it should be 2.0 type
+ if (session_type_2_1_ != SessionType_2_1::UNKNOWN) return audio_config_2_2_;
+
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration toConf;
+ const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig();
+ // pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration
+ if (fromConf.getDiscriminator() ==
+ AudioConfiguration::hidl_discriminator::codecConfig) {
+ toConf.codecConfig() = fromConf.codecConfig();
+ } else {
+ toConf.pcmConfig() = {
+ .sampleRate = static_cast<
+ ::android::hardware::bluetooth::audio::V2_1::SampleRate>(
+ fromConf.pcmConfig().sampleRate),
+ .channelMode = fromConf.pcmConfig().channelMode,
+ .bitsPerSample = fromConf.pcmConfig().bitsPerSample,
+ .dataIntervalUs = 0};
+ }
+ return toConf;
+ } else if (session_type_2_1_ ==
+ SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+ return kInvalidOffloadAudioConfiguration;
+ } else {
+ return kInvalidSoftwareAudioConfiguration;
+ }
+}
+
+bool BluetoothAudioSession_2_2::UpdateAudioConfig(
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+ audio_config) {
+ bool is_software_session =
+ (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_2_1_ ==
+ SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_2_1_ ==
+ SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
+ session_type_2_1_ ==
+ SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+ bool is_offload_a2dp_session =
+ (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+ bool is_offload_le_audio_session =
+ (session_type_2_1_ ==
+ SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
+ session_type_2_1_ ==
+ SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
+ auto audio_config_discriminator = audio_config.getDiscriminator();
+ bool is_software_audio_config =
+ (is_software_session &&
+ audio_config_discriminator ==
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration::
+ hidl_discriminator::pcmConfig);
+ bool is_a2dp_offload_audio_config =
+ (is_offload_a2dp_session &&
+ audio_config_discriminator ==
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration::
+ hidl_discriminator::codecConfig);
+ bool is_le_audio_offload_audio_config =
+ (is_offload_le_audio_session &&
+ audio_config_discriminator ==
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration::
+ hidl_discriminator::leAudioConfig);
+ if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
+ !is_le_audio_offload_audio_config) {
+ return false;
+ }
+ audio_config_2_2_ = audio_config;
+ return true;
+}
+
+// The report function is used to report that the Bluetooth stack has started
+// this session without any failure, and will invoke session_changed_cb_ to
+// notify those registered bluetooth_audio outputs
+void BluetoothAudioSession_2_2::OnSessionStarted(
+ const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+ audio_config) {
+ if (session_type_2_1_ == SessionType_2_1::UNKNOWN) {
+ ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config;
+ if (audio_config.getDiscriminator() ==
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration::
+ hidl_discriminator::codecConfig) {
+ config.codecConfig(audio_config.codecConfig());
+ } else {
+ auto& tmpPcm = audio_config.pcmConfig();
+ config.pcmConfig(
+ ::android::hardware::bluetooth::audio::V2_0::PcmParameters{
+ .sampleRate = static_cast<SampleRate>(tmpPcm.sampleRate),
+ .channelMode = tmpPcm.channelMode,
+ .bitsPerSample = tmpPcm.bitsPerSample
+ /*dataIntervalUs is not passed to 2.0 */
+ });
+ }
+
+ audio_session->OnSessionStarted(stack_iface, dataMQ, config);
+ } else {
+ std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+ if (stack_iface == nullptr) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << ", IBluetoothAudioPort Invalid";
+ } else if (!UpdateAudioConfig(audio_config)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << ", AudioConfiguration=" << toString(audio_config)
+ << " Invalid";
+ } else if (!audio_session->UpdateDataPath(dataMQ)) {
+ LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << " DataMQ Invalid";
+ audio_config_2_2_ =
+ (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH
+ ? kInvalidOffloadAudioConfiguration
+ : kInvalidSoftwareAudioConfiguration);
+ } else {
+ audio_session->stack_iface_ = stack_iface;
+ LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+ << ", AudioConfiguration=" << toString(audio_config);
+ audio_session->ReportSessionStatus();
+ };
+ }
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance_2_2>
+ BluetoothAudioSessionInstance_2_2::instance_ptr =
+ std::unique_ptr<BluetoothAudioSessionInstance_2_2>(
+ new BluetoothAudioSessionInstance_2_2());
+
+// API to fetch the session of A2DP / Hearing Aid
+std::shared_ptr<BluetoothAudioSession_2_2>
+BluetoothAudioSessionInstance_2_2::GetSessionInstance(
+ const SessionType_2_1& session_type) {
+ std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
+ if (!instance_ptr->sessions_map_.empty()) {
+ auto entry = instance_ptr->sessions_map_.find(session_type);
+ if (entry != instance_ptr->sessions_map_.end()) {
+ return entry->second;
+ }
+ }
+ std::shared_ptr<BluetoothAudioSession_2_2> session_ptr =
+ std::make_shared<BluetoothAudioSession_2_2>(session_type);
+ instance_ptr->sessions_map_[session_type] = session_ptr;
+ return session_ptr;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
new file mode 100644
index 0000000..d3d0bd3
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_2.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.2/types.h>
+
+#include <mutex>
+#include <unordered_map>
+
+#include "BluetoothAudioSession.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSession_2_2 {
+ private:
+ std::shared_ptr<BluetoothAudioSession> audio_session;
+
+ ::android::hardware::bluetooth::audio::V2_1::SessionType session_type_2_1_;
+
+ // audio data configuration for both software and offloading
+ ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ audio_config_2_2_;
+
+ bool UpdateAudioConfig(
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+ audio_config);
+
+ static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ invalidSoftwareAudioConfiguration;
+ static ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ invalidOffloadAudioConfiguration;
+
+ public:
+ BluetoothAudioSession_2_2(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type);
+
+ // The function helps to check if this session is ready or not
+ // @return: true if the Bluetooth stack has started the specified session
+ bool IsSessionReady();
+
+ std::shared_ptr<BluetoothAudioSession> GetAudioSession();
+
+ // The report function is used to report that the Bluetooth stack has started
+ // this session without any failure, and will invoke session_changed_cb_ to
+ // notify those registered bluetooth_audio outputs
+ void OnSessionStarted(
+ const sp<IBluetoothAudioPort> stack_iface,
+ const DataMQ::Descriptor* dataMQ,
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration&
+ audio_config);
+
+ // The control function is for the bluetooth_audio module to get the current
+ // AudioConfiguration
+ const ::android::hardware::bluetooth::audio::V2_2::AudioConfiguration
+ GetAudioConfig();
+
+ static constexpr ::android::hardware::bluetooth::audio::V2_2::
+ AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+ invalidSoftwareAudioConfiguration;
+ static constexpr ::android::hardware::bluetooth::audio::V2_2::
+ AudioConfiguration& kInvalidOffloadAudioConfiguration =
+ invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance_2_2 {
+ public:
+ // The API is to fetch the specified session of A2DP / Hearing Aid
+ static std::shared_ptr<BluetoothAudioSession_2_2> GetSessionInstance(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type);
+
+ private:
+ static std::unique_ptr<BluetoothAudioSessionInstance_2_2> instance_ptr;
+ std::mutex mutex_;
+ std::unordered_map<::android::hardware::bluetooth::audio::V2_1::SessionType,
+ std::shared_ptr<BluetoothAudioSession_2_2>>
+ sessions_map_;
+};
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp
new file mode 100644
index 0000000..5becdaa
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderSessionCodecsDB_2_2"
+
+#include "BluetoothAudioSupportedCodecsDB_2_2.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using SessionType_2_1 =
+ ::android::hardware::bluetooth::audio::V2_1::SessionType;
+
+bool IsOffloadLeAudioConfigurationValid(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type,
+ const ::android::hardware::bluetooth::audio::V2_2::LeAudioConfiguration&) {
+ if (session_type !=
+ SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+ session_type !=
+ SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+ return false;
+ }
+
+ // TODO: perform checks on le_audio_codec_config once we know supported
+ // parameters
+
+ return true;
+}
+
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h
new file mode 100644
index 0000000..59d22b7
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_2.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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/bluetooth/audio/2.2/types.h>
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+bool IsOffloadLeAudioConfigurationValid(
+ const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+ session_type,
+ const ::android::hardware::bluetooth::audio::V2_2::LeAudioConfiguration&
+ le_audio_codec_config);
+} // namespace audio
+} // namespace bluetooth
+} // namespace android
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 49e00f4..5c886ee 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -6197,14 +6197,13 @@
return;
}
- // Test that if more than one color cameras facing the same direction are
- // supported, there must be at least one logical camera facing that
- // direction.
+ // Test that if more than one rear-facing color camera is
+ // supported, there must be at least one rear-facing logical camera.
hidl_vec<hidl_string> cameraDeviceNames = getCameraDeviceNames(mProvider);
- // Front and back facing non-logical color cameras
- std::set<std::string> frontColorCameras, rearColorCameras;
- // Front and back facing logical cameras' physical camera Id sets
- std::set<std::set<std::string>> frontPhysicalIds, rearPhysicalIds;
+ // Back facing non-logical color cameras
+ std::set<std::string> rearColorCameras;
+ // Back facing logical cameras' physical camera Id sets
+ std::set<std::set<std::string>> rearPhysicalIds;
for (const auto& name : cameraDeviceNames) {
std::string cameraId;
int deviceVersion = getCameraDeviceVersionAndId(name, mProviderType, &cameraId);
@@ -6236,8 +6235,8 @@
return;
}
- // Check camera facing. Skip if facing is neither FRONT
- // nor BACK. If this is not a logical camera, only note down
+ // Check camera facing. Skip if facing is not BACK.
+ // If this is not a logical camera, only note down
// the camera ID, and skip.
camera_metadata_ro_entry entry;
int retcode = find_camera_metadata_ro_entry(
@@ -6246,18 +6245,12 @@
ASSERT_GT(entry.count, 0);
uint8_t facing = entry.data.u8[0];
bool isLogicalCamera = (isLogicalMultiCamera(metadata) == Status::OK);
- if (facing == ANDROID_LENS_FACING_FRONT) {
- if (!isLogicalCamera) {
- frontColorCameras.insert(cameraId);
- return;
- }
- } else if (facing == ANDROID_LENS_FACING_BACK) {
- if (!isLogicalCamera) {
- rearColorCameras.insert(cameraId);
- return;
- }
- } else {
- // Not FRONT or BACK facing. Skip.
+ if (facing != ANDROID_LENS_FACING_BACK) {
+ // Not BACK facing. Skip.
+ return;
+ }
+ if (!isLogicalCamera) {
+ rearColorCameras.insert(cameraId);
return;
}
@@ -6266,11 +6259,7 @@
std::unordered_set<std::string> physicalCameraIds;
Status s = getPhysicalCameraIds(metadata, &physicalCameraIds);
ASSERT_EQ(Status::OK, s);
- if (facing == ANDROID_LENS_FACING_FRONT) {
- frontPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
- } else {
- rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
- }
+ rearPhysicalIds.emplace(physicalCameraIds.begin(), physicalCameraIds.end());
for (const auto& physicalId : physicalCameraIds) {
// Skip if the physicalId is publicly available
for (auto& deviceName : cameraDeviceNames) {
@@ -6297,11 +6286,7 @@
(camera_metadata_t*)chars.data();
if (CameraHidlTest::isColorCamera(physicalMetadata)) {
- if (facing == ANDROID_LENS_FACING_FRONT) {
- frontColorCameras.insert(physicalId);
- } else if (facing == ANDROID_LENS_FACING_BACK) {
- rearColorCameras.insert(physicalId);
- }
+ rearColorCameras.insert(physicalId);
}
});
ASSERT_TRUE(ret.isOk());
@@ -6319,20 +6304,9 @@
}
}
- // If there are more than one color cameras facing one direction, a logical
- // multi-camera must be defined consisting of all color cameras facing that
- // direction.
- if (frontColorCameras.size() > 1) {
- bool hasFrontLogical = false;
- for (const auto& physicalIds : frontPhysicalIds) {
- if (std::includes(physicalIds.begin(), physicalIds.end(),
- frontColorCameras.begin(), frontColorCameras.end())) {
- hasFrontLogical = true;
- break;
- }
- }
- ASSERT_TRUE(hasFrontLogical);
- }
+ // If there are more than one rear-facing color camera, a logical
+ // multi-camera must be defined consisting of all rear-facing color
+ // cameras.
if (rearColorCameras.size() > 1) {
bool hasRearLogical = false;
for (const auto& physicalIds : rearPhysicalIds) {
diff --git a/cas/1.0/vts/functional/OWNERS b/cas/1.0/vts/functional/OWNERS
index aec93b0..7d8c2ee 100644
--- a/cas/1.0/vts/functional/OWNERS
+++ b/cas/1.0/vts/functional/OWNERS
@@ -1,2 +1,2 @@
# Bug component: 1344
-quxiangfang@google.com
+include ../../../1.2/vts/functional/OWNERS
diff --git a/cas/1.1/vts/functional/OWNERS b/cas/1.1/vts/functional/OWNERS
index 29246ed..7d8c2ee 100644
--- a/cas/1.1/vts/functional/OWNERS
+++ b/cas/1.1/vts/functional/OWNERS
@@ -1,3 +1,2 @@
-nchalko@google.com
-chz@google.com
-quxiangfang@google.com
+# Bug component: 1344
+include ../../../1.2/vts/functional/OWNERS
diff --git a/cas/1.2/vts/functional/OWNERS b/cas/1.2/vts/functional/OWNERS
index 29246ed..4c55752 100644
--- a/cas/1.2/vts/functional/OWNERS
+++ b/cas/1.2/vts/functional/OWNERS
@@ -1,3 +1,3 @@
-nchalko@google.com
-chz@google.com
+# Bug component: 1344
quxiangfang@google.com
+hgchen@google.com
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 0c7e58e..ece4de7 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -133,7 +133,7 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.bluetooth.audio</name>
- <version>2.0-1</version>
+ <version>2.0-2</version>
<interface>
<name>IBluetoothAudioProvidersFactory</name>
<instance>default</instance>
@@ -276,6 +276,15 @@
<instance>default</instance>
</interface>
</hal>
+ <!-- TODO(b/177269435): require health AIDL HAL and deprecate HIDL HAL -->
+ <hal format="aidl" optional="true">
+ <name>android.hardware.health</name>
+ <version>1</version>
+ <interface>
+ <name>IHealth</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.health.storage</name>
<version>1</version>
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.2/vts/functional/OWNERS
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.3/vts/functional/OWNERS
+++ b/graphics/composer/2.3/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS
index ea06752..31b0dc7 100644
--- a/graphics/composer/2.4/vts/functional/OWNERS
+++ b/graphics/composer/2.4/vts/functional/OWNERS
@@ -1,7 +1,5 @@
+# Bug component: 25423
# Graphics team
adyabr@google.com
lpy@google.com
-
-# VTS team
-yim@google.com
-zhuoyao@google.com
+sumir@google.com
diff --git a/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp b/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
index 9e1cc70..3c353e6 100644
--- a/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
+++ b/health/2.0/utils/libhealthhalutils/HealthHalUtils.cpp
@@ -25,7 +25,26 @@
namespace V2_0 {
sp<IHealth> get_health_service() {
- for (auto&& instanceName : {"default", "backup"}) {
+ // For the core and vendor variant, the "backup" instance points to healthd,
+ // which is removed.
+ // For the recovery variant, the "backup" instance has a different
+ // meaning. It points to android.hardware.health@2.0-impl-default.recovery
+ // which was assumed by OEMs to be always installed when a
+ // vendor-specific libhealthd is not necessary. Hence, its behavior
+ // is kept. See health/2.0/README.md.
+ // android.hardware.health@2.0-impl-default.recovery, and subsequently the
+ // special handling of recovery mode below, can be removed once health@2.1
+ // is the minimum required version (i.e. compatibility matrix level 5 is the
+ // minimum supported level). Health 2.1 requires OEMs to install the
+ // implementation to the recovery partition when it is necessary (i.e. on
+ // non-A/B devices, where IsBatteryOk() is needed in recovery).
+ for (auto&& instanceName :
+#ifdef __ANDROID_RECOVERY__
+ { "default", "backup" }
+#else
+ {"default"}
+#endif
+ ) {
auto ret = IHealth::getService(instanceName);
if (ret != nullptr) {
return ret;
diff --git a/health/2.0/vts/functional/Android.bp b/health/2.0/vts/functional/Android.bp
index eb69612..0fcac19 100644
--- a/health/2.0/vts/functional/Android.bp
+++ b/health/2.0/vts/functional/Android.bp
@@ -32,5 +32,11 @@
"android.hardware.health@1.0",
"android.hardware.health@2.0",
],
- test_suites: ["general-tests", "vts"],
+ header_libs: [
+ "libhealthtest_headers",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
}
diff --git a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
index 7fabf2b..3afba45 100644
--- a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
+++ b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
@@ -29,6 +29,7 @@
#include <android/hardware/health/2.0/types.h>
#include <gflags/gflags.h>
#include <gtest/gtest.h>
+#include <health-test/TestUtils.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#include <log/log.h>
@@ -51,6 +52,7 @@
namespace hardware {
namespace health {
+using test_utils::SucceedOnce;
using V1_0::BatteryStatus;
using V1_0::toString;
@@ -356,64 +358,9 @@
<< toString(current.result) << ", skipping";
}
- // For IHealth.getCurrentNow/Average, if current is not available, it is expected that
- // current.result == Result::NOT_SUPPORTED, which is checked above. Hence, zero current is
- // not treated as unknown values.
- // For IHealth.getHealthInfo, if current is not available, health_info.current_* == 0.
- // Caller of this function provides current.result == Result::SUCCESS. Hence, just skip the
- // check.
- if (current.value == 0 && acceptZeroCurrentAsUnknown) {
- return AssertionSuccess()
- << "current is 0, which indicates the value may not be available. Skipping.";
- }
-
- switch (status.value) {
- case BatteryStatus::UNKNOWN:
- if (current.value != 0) {
- // BatteryStatus may be UNKNOWN initially with a non-zero current value, but
- // after it is initialized, it should be known.
- return AssertionFailure()
- << "BatteryStatus is UNKNOWN but current is not 0. Actual: "
- << current.value;
- }
- break;
- case BatteryStatus::CHARGING:
- if (current.value <= 0) {
- return AssertionFailure()
- << "BatteryStatus is CHARGING but current is not positive. Actual: "
- << current.value;
- }
- break;
- case BatteryStatus::NOT_CHARGING:
- if (current.value > 0) {
- return AssertionFailure() << "BatteryStatus is " << toString(status.value)
- << " but current is positive. Actual: " << current.value;
- }
- break;
- case BatteryStatus::DISCHARGING:
- if (current.value >= 0) {
- return AssertionFailure()
- << "BatteryStatus is " << toString(status.value)
- << " but current is not negative. Actual: " << current.value;
- }
- break;
- case BatteryStatus::FULL:
- // Battery current may be positive or negative depending on the load.
- break;
- default:
- return AssertionFailure() << "Unknown BatteryStatus " << toString(status.value);
- }
-
- return AssertionSuccess() << "BatteryStatus is " << toString(status.value)
- << " and current has the correct sign: " << current.value;
-}
-
-static AssertionResult IsValueSimilar(int32_t dividend, int32_t divisor, double factor) {
- auto difference = abs(dividend - divisor);
- if (difference > factor * abs(divisor)) {
- return AssertionFailure() << dividend << " and " << divisor << " are not similar.";
- }
- return AssertionSuccess() << dividend << " and " << divisor << " are similar.";
+ return test_utils::IsBatteryCurrentSignCorrect(
+ status.value, current.value, acceptZeroCurrentAsUnknown,
+ [](BatteryStatus status) { return toString(status); });
}
static AssertionResult IsBatteryCurrentSimilar(HalResult<BatteryStatus> status,
@@ -437,31 +384,8 @@
<< currentAverage.value << ", skipping";
}
- // Check that the two values are similar. Note that the two tests uses a different
- // divisor to ensure that they are actually pretty similar. For example,
- // IsValueSimilar(5,10,0.4) returns true, but IsValueSimlar(10,5,0.4) returns false.
- TEST_AND_RETURN(IsValueSimilar(currentNow.value, currentAverage.value, gCurrentCompareFactor)
- << " for now vs. average. Check units.");
- TEST_AND_RETURN(IsValueSimilar(currentAverage.value, currentNow.value, gCurrentCompareFactor)
- << " for average vs. now. Check units.");
- return AssertionSuccess() << "currentNow = " << currentNow.value
- << " and currentAverage = " << currentAverage.value
- << " are considered similar.";
-}
-
-// Test that f() returns AssertionSuccess() once in a given period of time.
-template <typename Duration, typename Function>
-static AssertionResult SucceedOnce(Duration d, Function f) {
- AssertionResult result = AssertionFailure() << "Function never evaluated.";
- auto end = std::chrono::system_clock::now() + d;
- while (std::chrono::system_clock::now() <= end) {
- result = f();
- if (result) {
- return result;
- }
- std::this_thread::sleep_for(2s);
- }
- return result;
+ return test_utils::IsBatteryCurrentSimilar(currentNow.value, currentAverage.value,
+ gCurrentCompareFactor);
}
uint64_t GetShippingApiLevel() {
@@ -603,40 +527,8 @@
}
const auto& batteryInfo = healthInfo.value.legacy;
- bool isConnected = batteryInfo.chargerAcOnline || batteryInfo.chargerUsbOnline ||
- batteryInfo.chargerWirelessOnline;
-
- std::stringstream message;
- message << "BatteryStatus is " << toString(status.value) << " and "
- << (isConnected ? "" : "no ")
- << "power source is connected: ac=" << batteryInfo.chargerAcOnline
- << ", usb=" << batteryInfo.chargerUsbOnline
- << ", wireless=" << batteryInfo.chargerWirelessOnline;
-
- switch (status.value) {
- case BatteryStatus::UNKNOWN: {
- // Don't enforce anything on isConnected on unknown battery status.
- // Battery-less devices must report UNKNOWN battery status, but may report true
- // or false on isConnected.
- } break;
- case BatteryStatus::CHARGING:
- case BatteryStatus::NOT_CHARGING:
- case BatteryStatus::FULL: {
- if (!isConnected) {
- return AssertionFailure() << message.str();
- }
- } break;
- case BatteryStatus::DISCHARGING: {
- if (isConnected) {
- return AssertionFailure() << message.str();
- }
- } break;
- default: {
- return AssertionFailure() << "Unknown battery status value " << toString(status.value);
- } break;
- }
-
- return AssertionSuccess() << message.str();
+ return test_utils::IsBatteryStatusCorrect(
+ status.value, batteryInfo, [](BatteryStatus status) { return toString(status); });
}
TEST_P(BatteryTest, ConnectedAgainstStatusFromHal) {
diff --git a/health/aidl/Android.bp b/health/aidl/Android.bp
new file mode 100644
index 0000000..fae7592
--- /dev/null
+++ b/health/aidl/Android.bp
@@ -0,0 +1,74 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+ name: "android.hardware.health",
+ vendor_available: true,
+ recovery_available: true,
+ srcs: ["android/hardware/health/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: true,
+ sdk_version: "module_current",
+ },
+ ndk: {
+ separate_platform_variant: false,
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
+
+cc_library {
+ name: "android.hardware.health-translate-ndk",
+ vendor_available: true,
+ recovery_available: true,
+ srcs: ["android/hardware/health/translate-ndk.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ "libhidlbase",
+ "android.hardware.health-V1-ndk",
+ "android.hardware.health@2.0",
+ "android.hardware.health@2.1",
+ ],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: [
+ "android.hardware.health@2.0",
+ "android.hardware.health@2.1",
+ ],
+}
+
+java_library {
+ name: "android.hardware.health-translate-java",
+ srcs: ["android/hardware/health/Translate.java"],
+ libs: [
+ "android.hardware.health-V1-java",
+ "android.hardware.health-V2.0-java",
+ "android.hardware.health-V2.1-java",
+ ],
+}
diff --git a/health/aidl/OWNERS b/health/aidl/OWNERS
new file mode 100644
index 0000000..cd06415
--- /dev/null
+++ b/health/aidl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 30545
+elsk@google.com
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl
new file mode 100644
index 0000000..e543886
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryCapacityLevel.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryCapacityLevel {
+ UNSUPPORTED = -1,
+ UNKNOWN = 0,
+ CRITICAL = 1,
+ LOW = 2,
+ NORMAL = 3,
+ HIGH = 4,
+ FULL = 5,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl
new file mode 100644
index 0000000..4ce7952
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryHealth.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryHealth {
+ UNKNOWN = 1,
+ GOOD = 2,
+ OVERHEAT = 3,
+ DEAD = 4,
+ OVER_VOLTAGE = 5,
+ UNSPECIFIED_FAILURE = 6,
+ COLD = 7,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl
new file mode 100644
index 0000000..340b2ec
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/BatteryStatus.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@Backing(type="int") @VintfStability
+enum BatteryStatus {
+ UNKNOWN = 1,
+ CHARGING = 2,
+ DISCHARGING = 3,
+ NOT_CHARGING = 4,
+ FULL = 5,
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl
new file mode 100644
index 0000000..5aa5890
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/DiskStats.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@VintfStability
+parcelable DiskStats {
+ long reads;
+ long readMerges;
+ long readSectors;
+ long readTicks;
+ long writes;
+ long writeMerges;
+ long writeSectors;
+ long writeTicks;
+ long ioInFlight;
+ long ioTicks;
+ long ioInQueue;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
new file mode 100644
index 0000000..34a87a6
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/HealthInfo.aidl
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@VintfStability
+parcelable HealthInfo {
+ boolean chargerAcOnline;
+ boolean chargerUsbOnline;
+ boolean chargerWirelessOnline;
+ int maxChargingCurrentMicroamps;
+ int maxChargingVoltageMicrovolts;
+ android.hardware.health.BatteryStatus batteryStatus;
+ android.hardware.health.BatteryHealth batteryHealth;
+ boolean batteryPresent;
+ int batteryLevel;
+ int batteryVoltageMillivolts;
+ int batteryTemperatureTenthsCelsius;
+ int batteryCurrentMicroamps;
+ int batteryCycleCount;
+ int batteryFullChargeUah;
+ int batteryChargeCounterUah;
+ String batteryTechnology;
+ int batteryCurrentAverageMicroamps;
+ android.hardware.health.DiskStats[] diskStats;
+ android.hardware.health.StorageInfo[] storageInfos;
+ android.hardware.health.BatteryCapacityLevel batteryCapacityLevel;
+ long batteryChargeTimeToFullNowSeconds;
+ int batteryFullChargeDesignCapacityUah;
+ const int BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl
new file mode 100644
index 0000000..7016ae4
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealth.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@VintfStability
+interface IHealth {
+ void registerCallback(in android.hardware.health.IHealthInfoCallback callback);
+ void unregisterCallback(in android.hardware.health.IHealthInfoCallback callback);
+ void update();
+ int getChargeCounterUah();
+ int getCurrentNowMicroamps();
+ int getCurrentAverageMicroamps();
+ int getCapacity();
+ long getEnergyCounterNwh();
+ android.hardware.health.BatteryStatus getChargeStatus();
+ android.hardware.health.StorageInfo[] getStorageInfo();
+ android.hardware.health.DiskStats[] getDiskStats();
+ android.hardware.health.HealthInfo getHealthInfo();
+ const int STATUS_UNKNOWN = 2;
+ const int STATUS_CALLBACK_DIED = 4;
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl
new file mode 100644
index 0000000..1b6366f
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/IHealthInfoCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@VintfStability
+interface IHealthInfoCallback {
+ oneway void healthInfoChanged(in android.hardware.health.HealthInfo info);
+}
diff --git a/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl
new file mode 100644
index 0000000..eaae5a6
--- /dev/null
+++ b/health/aidl/aidl_api/android.hardware.health/current/android/hardware/health/StorageInfo.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.health;
+@VintfStability
+parcelable StorageInfo {
+ int eol;
+ int lifetimeA;
+ int lifetimeB;
+ String version;
+}
diff --git a/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl b/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl
new file mode 100644
index 0000000..0c26fa2
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryCapacityLevel.aidl
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+/**
+ * Battery capacity level. This enum provides additional information along side
+ * with the battery capacity.
+ * Clients of this HAL must use this value before inferring it from the
+ * battery capacity.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryCapacityLevel {
+ /**
+ * Battery capacity level is unsupported.
+ * Battery capacity level must be set to this value if and only if the
+ * implementation is unsupported.
+ */
+ UNSUPPORTED = -1,
+ /**
+ * Battery capacity level is unknown.
+ * Battery capacity level must be set to this value if and only if battery
+ * is not present or the battery capacity level is unknown/uninitialized.
+ */
+ UNKNOWN,
+ /**
+ * Battery is at critical level. The Android framework must schedule a
+ * shutdown when it sees this value from the HAL.
+ */
+ CRITICAL,
+ /**
+ * Battery is low. The Android framework may limit the performance of
+ * the device when it sees this value from the HAL.
+ */
+ LOW,
+ /**
+ * Battery level is normal.
+ */
+ NORMAL,
+ /**
+ * Battery level is high.
+ */
+ HIGH,
+ /**
+ * Battery is full. It must be set to FULL if and only if battery level is
+ * 100.
+ */
+ FULL,
+}
diff --git a/health/aidl/android/hardware/health/BatteryHealth.aidl b/health/aidl/android/hardware/health/BatteryHealth.aidl
new file mode 100644
index 0000000..2b6e51f
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryHealth.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;
+
+/**
+ * Possible values for Battery Health.
+ * Note: These are currently in sync with BatteryManager and must not
+ * be extended / altered.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryHealth {
+ UNKNOWN = 1,
+ GOOD = 2,
+ OVERHEAT = 3,
+ DEAD = 4,
+ OVER_VOLTAGE = 5,
+ /**
+ * Battery experienced an unknown/unspecified failure.
+ */
+ UNSPECIFIED_FAILURE = 6,
+ COLD = 7,
+}
diff --git a/health/aidl/android/hardware/health/BatteryStatus.aidl b/health/aidl/android/hardware/health/BatteryStatus.aidl
new file mode 100644
index 0000000..774b28e
--- /dev/null
+++ b/health/aidl/android/hardware/health/BatteryStatus.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Possible values for Battery Status.
+ * Note: These are currently in sync with BatteryManager and must not
+ * be extended / altered.
+ */
+@VintfStability
+@Backing(type="int")
+enum BatteryStatus {
+ UNKNOWN = 1,
+ CHARGING = 2,
+ DISCHARGING = 3,
+ /**
+ * Battery is *not* charging - special case when charger is present
+ * but battery isn't charging
+ */
+ NOT_CHARGING = 4,
+ FULL = 5,
+}
diff --git a/health/aidl/android/hardware/health/DiskStats.aidl b/health/aidl/android/hardware/health/DiskStats.aidl
new file mode 100644
index 0000000..532cbfd
--- /dev/null
+++ b/health/aidl/android/hardware/health/DiskStats.aidl
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+/*
+ * Disk statistics since boot.
+ *
+ * See {@code struct disk_stats} in {@code storaged} for interpretations of these fields.
+ *
+ * All integers in this struct must be interpreted as unsigned.
+ */
+@VintfStability
+parcelable DiskStats {
+ /**
+ * Number of reads processed.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long reads;
+ /**
+ * number of read I/Os merged with in-queue I/Os.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long readMerges;
+ /**
+ * number of sectors read.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long readSectors;
+ /**
+ * total wait time for read requests.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long readTicks;
+ /**
+ * number of writes processed.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long writes;
+ /**
+ * number of writes merged with in-queue I/Os.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long writeMerges;
+ /**
+ * number of sectors written.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long writeSectors;
+ /**
+ * total wait time for write requests.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long writeTicks;
+ /**
+ * number of I/Os currently in flight.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long ioInFlight;
+ /**
+ * total time this block device has been active.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long ioTicks;
+ /**
+ * total wait time for all requests.
+ *
+ * Value must be interpreted as unsigned.
+ */
+ long ioInQueue;
+}
diff --git a/health/aidl/android/hardware/health/HealthInfo.aidl b/health/aidl/android/hardware/health/HealthInfo.aidl
new file mode 100644
index 0000000..504e218
--- /dev/null
+++ b/health/aidl/android/hardware/health/HealthInfo.aidl
@@ -0,0 +1,132 @@
+/*
+ * 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;
+
+import android.hardware.health.BatteryCapacityLevel;
+import android.hardware.health.BatteryHealth;
+import android.hardware.health.BatteryStatus;
+import android.hardware.health.DiskStats;
+import android.hardware.health.StorageInfo;
+
+/**
+ * Health Information.
+ */
+@VintfStability
+parcelable HealthInfo {
+ /**
+ * AC charger state - 'true' if online
+ */
+ boolean chargerAcOnline;
+ /**
+ * USB charger state - 'true' if online
+ */
+ boolean chargerUsbOnline;
+ /**
+ * Wireless charger state - 'true' if online
+ */
+ boolean chargerWirelessOnline;
+ /**
+ * Maximum charging current supported by charger in µA
+ */
+ int maxChargingCurrentMicroamps;
+ /**
+ * Maximum charging voltage supported by charger in µV
+ */
+ int maxChargingVoltageMicrovolts;
+
+ android.hardware.health.BatteryStatus batteryStatus;
+
+ android.hardware.health.BatteryHealth batteryHealth;
+ /**
+ * 'true' if battery is present
+ */
+ boolean batteryPresent;
+ /**
+ * Remaining battery capacity in percent
+ */
+ int batteryLevel;
+ /**
+ * Instantaneous battery voltage in millivolts (mV).
+ *
+ * Historically, the unit of this field is microvolts (µV), but all
+ * clients and implementations uses millivolts in practice, making it
+ * the de-facto standard.
+ */
+ int batteryVoltageMillivolts;
+ /**
+ * Instantaneous battery temperature in tenths of degrees Celsius
+ */
+ int batteryTemperatureTenthsCelsius;
+ /**
+ * Instantaneous battery current in µA
+ */
+ int batteryCurrentMicroamps;
+ /**
+ * Battery charge cycle count
+ */
+ int batteryCycleCount;
+ /**
+ * Battery charge value when it is considered to be "full" in µA-h
+ */
+ int batteryFullChargeUah;
+ /**
+ * Instantaneous battery capacity in µA-h
+ */
+ int batteryChargeCounterUah;
+ /**
+ * Battery technology, e.g. "Li-ion, Li-Poly" etc.
+ */
+ String batteryTechnology;
+ /**
+ * Average battery current in µA. Will be 0 if unsupported.
+ */
+ int batteryCurrentAverageMicroamps;
+ /**
+ * Disk Statistics. Will be an empty vector if unsupported.
+ */
+ DiskStats[] diskStats;
+ /**
+ * Information on storage devices. Will be an empty vector if
+ * unsupported.
+ */
+ StorageInfo[] storageInfos;
+ /**
+ * Battery capacity level. See {@link BatteryCapacityLevel} for more details.
+ */
+ BatteryCapacityLevel batteryCapacityLevel;
+
+ /**
+ * Value of {@link #batteryChargeTimeToFullNowSeconds} if it is not
+ * supported.
+ */
+ const int BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1;
+ /**
+ * Estimated time to fully charge the device (in seconds).
+ * Value must be BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED if and
+ * only if the implementation is unsupported.
+ * Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN.
+ * Otherwise, value must be positive.
+ */
+ long batteryChargeTimeToFullNowSeconds;
+ /**
+ * Estimated battery full charge design capacity (in microamp hours, µAh).
+ * Value must be 0 if unknown.
+ * Value must be greater than 100 000 µAh if known.
+ * Value must be less than 100 000 000 µAh if known.
+ */
+ int batteryFullChargeDesignCapacityUah;
+}
diff --git a/health/aidl/android/hardware/health/IHealth.aidl b/health/aidl/android/hardware/health/IHealth.aidl
new file mode 100644
index 0000000..d541eca
--- /dev/null
+++ b/health/aidl/android/hardware/health/IHealth.aidl
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+import android.hardware.health.BatteryStatus;
+import android.hardware.health.DiskStats;
+import android.hardware.health.HealthInfo;
+import android.hardware.health.IHealthInfoCallback;
+import android.hardware.health.StorageInfo;
+
+/**
+ * IHealth manages health info and posts events on registered callbacks.
+ *
+ * Implementations must send health info to all callbacks periodically.
+ */
+@VintfStability
+interface IHealth {
+ /** Status code for function. The operation encounters an unknown error. */
+ const int STATUS_UNKNOWN = 2;
+
+ /**
+ * Status code for function.
+ * A registered callback object is dead.
+ */
+ const int STATUS_CALLBACK_DIED = 4;
+
+ /**
+ * Register a callback for any health info events.
+ *
+ * Registering a new callback must not unregister the old one; the old
+ * callback remains registered until one of the following happens:
+ * - A client explicitly calls {@link #unregisterCallback} to unregister it.
+ * - The client process that hosts the callback dies.
+ *
+ * @param callback the callback to register.
+ * @return If error, return service specific error with code STATUS_UNKNOWN.
+ */
+ void registerCallback(in IHealthInfoCallback callback);
+
+ /**
+ * Explicitly unregister a callback that is previously registered through
+ * {@link #registerCallback}.
+ *
+ * @param callback the callback to unregister.
+ * @return If error:
+ * - Return exception with code EX_ILLEGAL_ARGUMENT
+ * if callback is not registered previously,
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for other errors.
+ */
+ void unregisterCallback(in IHealthInfoCallback callback);
+
+ /**
+ * Schedule update.
+ *
+ * When update() is called, the service must notify all registered callbacks
+ * with the most recent health info.
+ *
+ * @return If error, return service specific error with code:
+ * - STATUS_CALLBACK_DIED if any registered callback is dead,
+ * - STATUS_UNKNOWN for other errors.
+ */
+ void update();
+
+ /**
+ * Get battery capacity in microampere-hours(µAh).
+ *
+ * @return battery capacity if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported
+ * (e.g. the file that stores this property does not exist),
+ * - Retrurn service specific error with code
+ * STATUS_UNKNOWN for other errors.
+ */
+ int getChargeCounterUah();
+
+ /**
+ * Get instantaneous battery current in microamperes(µA).
+ *
+ * Positive values indicate net current entering the battery from a charge
+ * source, negative values indicate net current discharging from the
+ * battery.
+ *
+ * @return instantaneous battery current if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported
+ * (e.g. the file that stores this property does not exist),
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for for other errors.
+ */
+ int getCurrentNowMicroamps();
+
+ /**
+ * Get average battery current in microamperes(µA).
+ *
+ * Positive values indicate net current entering the battery from a charge
+ * source, negative values indicate net current discharging from the
+ * battery. The time period over which the average is computed may depend on
+ * the fuel gauge hardware and its configuration.
+ *
+ * @return average battery current if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported
+ * (e.g. the file that stores this property does not exist),
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for for other errors.
+ */
+ int getCurrentAverageMicroamps();
+
+ /**
+ * Get remaining battery capacity percentage of total capacity
+ * (with no fractional part).
+ *
+ * @return remaining battery capacity if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported
+ * (e.g. the file that stores this property does not exist),
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for for other errors.
+ */
+ int getCapacity();
+
+ /**
+ * Get battery remaining energy in nanowatt-hours.
+ *
+ * @return remaining energy if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported,
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for for other errors.
+ */
+ long getEnergyCounterNwh();
+
+ /**
+ * Get battery charge status.
+ *
+ * @return charge status if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported
+ * (e.g. the file that stores this property does not exist),
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for other errors.
+ */
+ BatteryStatus getChargeStatus();
+
+ /**
+ * Get storage info.
+ *
+ * @return vector of StorageInfo structs if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported,
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for other errors.
+ */
+ StorageInfo[] getStorageInfo();
+
+ /**
+ * Gets disk statistics (number of reads/writes processed, number of I/O
+ * operations in flight etc).
+ *
+ * @return vector of disk statistics if successful.
+ * The mapping is index 0->sda, 1->sdb and so on.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this property is not supported,
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for other errors.
+ */
+ DiskStats[] getDiskStats();
+
+ /**
+ * Get Health Information.
+ *
+ * @return Health information if successful.
+ * If error:
+ * - Return exception with code EX_UNSUPPORTED_OPERATION
+ * if this API is not supported,
+ * - Return service specific error with code STATUS_UNKNOWN
+ * for for other errors.
+ */
+ HealthInfo getHealthInfo();
+}
diff --git a/health/aidl/android/hardware/health/IHealthInfoCallback.aidl b/health/aidl/android/hardware/health/IHealthInfoCallback.aidl
new file mode 100644
index 0000000..e9720fa
--- /dev/null
+++ b/health/aidl/android/hardware/health/IHealthInfoCallback.aidl
@@ -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.
+ */
+
+package android.hardware.health;
+
+import android.hardware.health.HealthInfo;
+
+/**
+ * IHealthInfoCallback is the updated callback interface to
+ * {@link IHealth#registerCallback}.
+ */
+@VintfStability
+interface IHealthInfoCallback {
+ /**
+ * An implementation of IHealth must call healthInfoChanged on all
+ * registered callbacks after health info changes.
+ * @param info the updated HealthInfo
+ */
+ oneway void healthInfoChanged(in HealthInfo info);
+}
diff --git a/health/aidl/android/hardware/health/StorageInfo.aidl b/health/aidl/android/hardware/health/StorageInfo.aidl
new file mode 100644
index 0000000..4c4dace
--- /dev/null
+++ b/health/aidl/android/hardware/health/StorageInfo.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;
+
+/*
+ * Information on storage device including life time estimates, end of life
+ * information and other attributes.
+ *
+ * All integers in this struct must be interpreted as non-negative.
+ */
+@VintfStability
+parcelable StorageInfo {
+ /**
+ * pre-eol (end of life) information. Follows JEDEC standard No.84-B50.
+ *
+ * Value must be interpreted as non-negative.
+ */
+ int eol;
+ /**
+ * device life time estimation (type A). Follows JEDEC standard No.84-B50.
+ *
+ * Value must be interpreted as non-negative.
+ */
+ int lifetimeA;
+ /**
+ * device life time estimation (type B). Follows JEDEC standard No.84-B50.
+ *
+ * Value must be interpreted as non-negative.
+ */
+ int lifetimeB;
+ /**
+ * version string
+ */
+ String version;
+}
diff --git a/health/aidl/android/hardware/health/Translate.java b/health/aidl/android/hardware/health/Translate.java
new file mode 100644
index 0000000..c8ace1c
--- /dev/null
+++ b/health/aidl/android/hardware/health/Translate.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+public class Translate {
+ static public android.hardware.health.StorageInfo h2aTranslate(
+ android.hardware.health.V2_0.StorageInfo in) {
+ android.hardware.health.StorageInfo out = new android.hardware.health.StorageInfo();
+ out.eol = in.eol;
+ out.lifetimeA = in.lifetimeA;
+ out.lifetimeB = in.lifetimeB;
+ out.version = in.version;
+ return out;
+ }
+
+ static public android.hardware.health.DiskStats h2aTranslate(
+ android.hardware.health.V2_0.DiskStats in) {
+ android.hardware.health.DiskStats out = new android.hardware.health.DiskStats();
+ out.reads = in.reads;
+ out.readMerges = in.readMerges;
+ out.readSectors = in.readSectors;
+ out.readTicks = in.readTicks;
+ out.writes = in.writes;
+ out.writeMerges = in.writeMerges;
+ out.writeSectors = in.writeSectors;
+ out.writeTicks = in.writeTicks;
+ out.ioInFlight = in.ioInFlight;
+ out.ioTicks = in.ioTicks;
+ out.ioInQueue = in.ioInQueue;
+ return out;
+ }
+
+ static public android.hardware.health.HealthInfo h2aTranslate(
+ android.hardware.health.V2_1.HealthInfo in) {
+ android.hardware.health.HealthInfo out = new android.hardware.health.HealthInfo();
+ out.chargerAcOnline = in.legacy.legacy.chargerAcOnline;
+ out.chargerUsbOnline = in.legacy.legacy.chargerUsbOnline;
+ out.chargerWirelessOnline = in.legacy.legacy.chargerWirelessOnline;
+ out.maxChargingCurrentMicroamps = in.legacy.legacy.maxChargingCurrent;
+ out.maxChargingVoltageMicrovolts = in.legacy.legacy.maxChargingVoltage;
+ out.batteryStatus = in.legacy.legacy.batteryStatus;
+ out.batteryHealth = in.legacy.legacy.batteryHealth;
+ out.batteryPresent = in.legacy.legacy.batteryPresent;
+ out.batteryLevel = in.legacy.legacy.batteryLevel;
+ out.batteryVoltageMillivolts = in.legacy.legacy.batteryVoltage;
+ out.batteryTemperatureTenthsCelsius = in.legacy.legacy.batteryTemperature;
+ out.batteryCurrentMicroamps = in.legacy.legacy.batteryCurrent;
+ out.batteryCycleCount = in.legacy.legacy.batteryCycleCount;
+ out.batteryFullChargeUah = in.legacy.legacy.batteryFullCharge;
+ out.batteryChargeCounterUah = in.legacy.legacy.batteryChargeCounter;
+ out.batteryTechnology = in.legacy.legacy.batteryTechnology;
+ out.batteryCurrentAverageMicroamps = in.legacy.batteryCurrentAverage;
+ out.diskStats = new android.hardware.health.DiskStats[in.legacy.diskStats.size()];
+ for (int i = 0; i < in.legacy.diskStats.size(); i++) {
+ out.diskStats[i] = h2aTranslate(in.legacy.diskStats.get(i));
+ }
+ out.storageInfos = new android.hardware.health.StorageInfo[in.legacy.storageInfos.size()];
+ for (int i = 0; i < in.legacy.storageInfos.size(); i++) {
+ out.storageInfos[i] = h2aTranslate(in.legacy.storageInfos.get(i));
+ }
+ out.batteryCapacityLevel = in.batteryCapacityLevel;
+ out.batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+ out.batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+ return out;
+ }
+}
diff --git a/health/aidl/android/hardware/health/translate-ndk.cpp b/health/aidl/android/hardware/health/translate-ndk.cpp
new file mode 100644
index 0000000..7fe6ced
--- /dev/null
+++ b/health/aidl/android/hardware/health/translate-ndk.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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/hardware/health/translate-ndk.h"
+
+namespace android::h2a {
+
+static_assert(aidl::android::hardware::health::BatteryStatus::UNKNOWN ==
+ static_cast<aidl::android::hardware::health::BatteryStatus>(
+ ::android::hardware::health::V1_0::BatteryStatus::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryStatus::CHARGING ==
+ static_cast<aidl::android::hardware::health::BatteryStatus>(
+ ::android::hardware::health::V1_0::BatteryStatus::CHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::DISCHARGING ==
+ static_cast<aidl::android::hardware::health::BatteryStatus>(
+ ::android::hardware::health::V1_0::BatteryStatus::DISCHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::NOT_CHARGING ==
+ static_cast<aidl::android::hardware::health::BatteryStatus>(
+ ::android::hardware::health::V1_0::BatteryStatus::NOT_CHARGING));
+static_assert(aidl::android::hardware::health::BatteryStatus::FULL ==
+ static_cast<aidl::android::hardware::health::BatteryStatus>(
+ ::android::hardware::health::V1_0::BatteryStatus::FULL));
+
+static_assert(aidl::android::hardware::health::BatteryHealth::UNKNOWN ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryHealth::GOOD ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::GOOD));
+static_assert(aidl::android::hardware::health::BatteryHealth::OVERHEAT ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::OVERHEAT));
+static_assert(aidl::android::hardware::health::BatteryHealth::DEAD ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::DEAD));
+static_assert(aidl::android::hardware::health::BatteryHealth::OVER_VOLTAGE ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::OVER_VOLTAGE));
+static_assert(aidl::android::hardware::health::BatteryHealth::UNSPECIFIED_FAILURE ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::UNSPECIFIED_FAILURE));
+static_assert(aidl::android::hardware::health::BatteryHealth::COLD ==
+ static_cast<aidl::android::hardware::health::BatteryHealth>(
+ ::android::hardware::health::V1_0::BatteryHealth::COLD));
+
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::UNSUPPORTED ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::UNSUPPORTED));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::UNKNOWN ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::UNKNOWN));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::CRITICAL ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::CRITICAL));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::LOW ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::LOW));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::NORMAL ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::NORMAL));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::HIGH ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::HIGH));
+static_assert(aidl::android::hardware::health::BatteryCapacityLevel::FULL ==
+ static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ ::android::hardware::health::V2_1::BatteryCapacityLevel::FULL));
+
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_0::StorageInfo& in,
+ aidl::android::hardware::health::StorageInfo* out) {
+ out->eol = in.eol;
+ out->lifetimeA = in.lifetimeA;
+ out->lifetimeB = in.lifetimeB;
+ out->version = in.version;
+ return true;
+}
+
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_0::DiskStats& in,
+ aidl::android::hardware::health::DiskStats* out) {
+ out->reads = static_cast<int64_t>(in.reads);
+ out->readMerges = static_cast<int64_t>(in.readMerges);
+ out->readSectors = static_cast<int64_t>(in.readSectors);
+ out->readTicks = static_cast<int64_t>(in.readTicks);
+ out->writes = static_cast<int64_t>(in.writes);
+ out->writeMerges = static_cast<int64_t>(in.writeMerges);
+ out->writeSectors = static_cast<int64_t>(in.writeSectors);
+ out->writeTicks = static_cast<int64_t>(in.writeTicks);
+ out->ioInFlight = static_cast<int64_t>(in.ioInFlight);
+ out->ioTicks = static_cast<int64_t>(in.ioTicks);
+ out->ioInQueue = static_cast<int64_t>(in.ioInQueue);
+ return true;
+}
+
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_1::HealthInfo& in,
+ aidl::android::hardware::health::HealthInfo* out) {
+ out->chargerAcOnline = static_cast<bool>(in.legacy.legacy.chargerAcOnline);
+ out->chargerUsbOnline = static_cast<bool>(in.legacy.legacy.chargerUsbOnline);
+ out->chargerWirelessOnline = static_cast<bool>(in.legacy.legacy.chargerWirelessOnline);
+ out->maxChargingCurrentMicroamps = static_cast<int32_t>(in.legacy.legacy.maxChargingCurrent);
+ out->maxChargingVoltageMicrovolts = static_cast<int32_t>(in.legacy.legacy.maxChargingVoltage);
+ out->batteryStatus = static_cast<aidl::android::hardware::health::BatteryStatus>(
+ in.legacy.legacy.batteryStatus);
+ out->batteryHealth = static_cast<aidl::android::hardware::health::BatteryHealth>(
+ in.legacy.legacy.batteryHealth);
+ out->batteryPresent = static_cast<bool>(in.legacy.legacy.batteryPresent);
+ out->batteryLevel = static_cast<int32_t>(in.legacy.legacy.batteryLevel);
+ out->batteryVoltageMillivolts = static_cast<int32_t>(in.legacy.legacy.batteryVoltage);
+ out->batteryTemperatureTenthsCelsius =
+ static_cast<int32_t>(in.legacy.legacy.batteryTemperature);
+ out->batteryCurrentMicroamps = static_cast<int32_t>(in.legacy.legacy.batteryCurrent);
+ out->batteryCycleCount = static_cast<int32_t>(in.legacy.legacy.batteryCycleCount);
+ out->batteryFullChargeUah = static_cast<int32_t>(in.legacy.legacy.batteryFullCharge);
+ out->batteryChargeCounterUah = static_cast<int32_t>(in.legacy.legacy.batteryChargeCounter);
+ out->batteryTechnology = in.legacy.legacy.batteryTechnology;
+ out->batteryCurrentAverageMicroamps = static_cast<int32_t>(in.legacy.batteryCurrentAverage);
+ out->diskStats.clear();
+ out->diskStats.resize(in.legacy.diskStats.size());
+ for (size_t i = 0; i < in.legacy.diskStats.size(); ++i)
+ if (!translate(in.legacy.diskStats[i], &out->diskStats[i])) return false;
+ out->storageInfos.clear();
+ out->storageInfos.resize(in.legacy.storageInfos.size());
+ for (size_t i = 0; i < in.legacy.storageInfos.size(); ++i)
+ if (!translate(in.legacy.storageInfos[i], &out->storageInfos[i])) return false;
+ out->batteryCapacityLevel = static_cast<aidl::android::hardware::health::BatteryCapacityLevel>(
+ in.batteryCapacityLevel);
+ out->batteryChargeTimeToFullNowSeconds =
+ static_cast<int64_t>(in.batteryChargeTimeToFullNowSeconds);
+ out->batteryFullChargeDesignCapacityUah =
+ static_cast<int32_t>(in.batteryFullChargeDesignCapacityUah);
+ return true;
+}
+
+} // namespace android::h2a
diff --git a/health/aidl/default/Android.bp b/health/aidl/default/Android.bp
new file mode 100644
index 0000000..7e635d4
--- /dev/null
+++ b/health/aidl/default/Android.bp
@@ -0,0 +1,86 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+ name: "libhealth_aidl_common_defaults",
+ recovery_available: true,
+ vendor: true,
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ "android.hardware.health-V1-ndk",
+
+ // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+ "libhidlbase",
+ ],
+ static_libs: [
+ "libbatterymonitor",
+ "libhealthloop",
+
+ // TODO(b/177269435): remove when BatteryMonitor works with AIDL HealthInfo.
+ "android.hardware.health-translate-ndk",
+ ],
+}
+
+// AIDL version of libhealth2impl.
+// A helper library for health HAL implementation.
+// HAL implementations can link to this library and extend the Health class.
+cc_library_static {
+ name: "libhealth_aidl_impl",
+ defaults: [
+ "libhealth_aidl_common_defaults",
+ ],
+ export_include_dirs: ["include"],
+ export_static_lib_headers: [
+ "libbatterymonitor",
+ ],
+ srcs: [
+ "health-convert.cpp",
+ "HalHealthLoop.cpp",
+ "Health.cpp",
+ "LinkedCallback.cpp",
+ ],
+ visibility: [
+ ":__subpackages__",
+ "//hardware/interfaces/tests/extension/health:__subpackages__",
+ ],
+}
+
+// AIDL version of android.hardware.health@2.1-service.
+// Default binder service of the health HAL.
+cc_binary {
+ name: "android.hardware.health-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.health-service.example.rc"],
+ vintf_fragments: ["android.hardware.health-service.example.xml"],
+ defaults: [
+ "libhealth_aidl_common_defaults",
+ ],
+ static_libs: [
+ "libhealth_aidl_impl",
+ ],
+ srcs: ["main.cpp"],
+}
diff --git a/health/aidl/default/HalHealthLoop.cpp b/health/aidl/default/HalHealthLoop.cpp
new file mode 100644
index 0000000..c9a081e
--- /dev/null
+++ b/health/aidl/default/HalHealthLoop.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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-impl/HalHealthLoop.h>
+
+#include <android-base/logging.h>
+
+#include <health-impl/Health.h>
+#include "health-convert.h"
+
+namespace aidl::android::hardware::health {
+
+// Unlike the HIDL version android::hardware::health::V2_1::implementation::HalHealthLoop,
+// do not define HalHealthLoop::Init because we no longer have Health::getHealthConfig.
+// Let the Health class handle Init.
+void HalHealthLoop::Init(struct healthd_config* config) {
+ callback_->OnInit(this, config);
+}
+
+void HalHealthLoop::Heartbeat() {
+ callback_->OnHeartbeat();
+}
+
+void HalHealthLoop::ScheduleBatteryUpdate() {
+ // ignore errors. impl may not be able to handle any callbacks, so
+ // update() may return errors.
+ if (auto res = service_->update(); !res.isOk()) {
+ LOG(WARNING) << "update() on the health HAL implementation failed with "
+ << res.getDescription();
+ }
+
+ HealthInfo health_info;
+ auto res = service_->getHealthInfo(&health_info);
+ CHECK(res.isOk()) << "getHealthInfo() on the health HAL implementation failed with "
+ << res.getDescription();
+ OnHealthInfoChanged(health_info);
+}
+
+int HalHealthLoop::PrepareToWait() {
+ return callback_->OnPrepareToWait();
+}
+
+void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
+ callback_->OnHealthInfoChanged(health_info);
+ set_charger_online(health_info);
+ AdjustWakealarmPeriods(charger_online());
+}
+
+void HalHealthLoop::set_charger_online(const HealthInfo& health_info) {
+ charger_online_ = health_info.chargerAcOnline || health_info.chargerUsbOnline ||
+ health_info.chargerWirelessOnline;
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/Health.cpp b/health/aidl/default/Health.cpp
new file mode 100644
index 0000000..812e64a
--- /dev/null
+++ b/health/aidl/default/Health.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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-impl/Health.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/hardware/health/translate-ndk.h>
+#include <health/utils.h>
+
+#include "LinkedCallback.h"
+#include "health-convert.h"
+
+using std::string_literals::operator""s;
+
+namespace aidl::android::hardware::health {
+
+namespace {
+// Wrap LinkedCallback::OnCallbackDied() into a void(void*).
+void OnCallbackDiedWrapped(void* cookie) {
+ LinkedCallback* linked = reinterpret_cast<LinkedCallback*>(cookie);
+ linked->OnCallbackDied();
+}
+} // namespace
+
+/*
+// If you need to call healthd_board_init, construct the Health instance with
+// the healthd_config after calling healthd_board_init:
+class MyHealth : public Health {
+ protected:
+ MyHealth() : Health(CreateConfig()) {}
+ private:
+ static std::unique_ptr<healthd_config> CreateConfig() {
+ auto config = std::make_unique<healthd_config>();
+ ::android::hardware::health::InitHealthdConfig(config.get());
+ healthd_board_init(config.get());
+ return std::move(config);
+ }
+};
+*/
+Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
+ : instance_name_(instance_name),
+ healthd_config_(std::move(config)),
+ death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
+ battery_monitor_.init(healthd_config_.get());
+}
+
+//
+// Getters.
+//
+
+template <typename T>
+static ndk::ScopedAStatus GetProperty(::android::BatteryMonitor* monitor, int id, T defaultValue,
+ T* out) {
+ *out = defaultValue;
+ struct ::android::BatteryProperty prop;
+ ::android::status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+ if (err == ::android::OK) {
+ *out = static_cast<T>(prop.valueInt64);
+ } else {
+ LOG(DEBUG) << "getProperty(" << id << ")"
+ << " fails: (" << err << ") " << ::android::statusToString(err);
+ }
+
+ switch (err) {
+ case ::android::OK:
+ return ndk::ScopedAStatus::ok();
+ case ::android::NAME_NOT_FOUND:
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ default:
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, ::android::statusToString(err).c_str());
+ }
+}
+
+ndk::ScopedAStatus Health::getChargeCounterUah(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CHARGE_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentNowMicroamps(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_NOW, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCurrentAverageMicroamps(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CURRENT_AVG, 0, out);
+}
+
+ndk::ScopedAStatus Health::getCapacity(int32_t* out) {
+ return GetProperty<int32_t>(&battery_monitor_, ::android::BATTERY_PROP_CAPACITY, 0, out);
+}
+
+ndk::ScopedAStatus Health::getEnergyCounterNwh(int64_t* out) {
+ return GetProperty<int64_t>(&battery_monitor_, ::android::BATTERY_PROP_ENERGY_COUNTER, 0, out);
+}
+
+ndk::ScopedAStatus Health::getChargeStatus(BatteryStatus* out) {
+ return GetProperty(&battery_monitor_, ::android::BATTERY_PROP_BATTERY_STATUS,
+ BatteryStatus::UNKNOWN, out);
+}
+
+ndk::ScopedAStatus Health::getDiskStats(std::vector<DiskStats>*) {
+ // This implementation does not support DiskStats. An implementation may extend this
+ // class and override this function to support disk stats.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getStorageInfo(std::vector<StorageInfo>*) {
+ // This implementation does not support StorageInfo. An implementation may extend this
+ // class and override this function to support storage info.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {
+ battery_monitor_.updateValues();
+
+ // TODO(b/177269435): BatteryMonitor should store AIDL HealthInfo instead.
+ auto health_info_2_1 = battery_monitor_.getHealthInfo_2_1();
+ if (!::android::h2a::translate(health_info_2_1, out)) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, "Cannot translate HIDL HealthInfo to AIDL");
+ }
+
+ // Fill in storage infos; these aren't retrieved by BatteryMonitor.
+ if (auto res = getStorageInfo(&out->storageInfos); !res.isOk()) {
+ if (res.getServiceSpecificError() == 0 &&
+ res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN,
+ ("getStorageInfo fails: " + res.getDescription()).c_str());
+ }
+ LOG(DEBUG) << "getHealthInfo: getStorageInfo fails with service-specific error, clearing: "
+ << res.getDescription();
+ out->storageInfos = {};
+ }
+ if (auto res = getDiskStats(&out->diskStats); !res.isOk()) {
+ if (res.getServiceSpecificError() == 0 &&
+ res.getExceptionCode() != EX_UNSUPPORTED_OPERATION) {
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN,
+ ("getDiskStats fails: " + res.getDescription()).c_str());
+ }
+ LOG(DEBUG) << "getHealthInfo: getDiskStats fails with service-specific error, clearing: "
+ << res.getDescription();
+ out->diskStats = {};
+ }
+
+ // A subclass may want to update health info struct before returning it.
+ UpdateHealthInfo(out);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+binder_status_t Health::dump(int fd, const char**, uint32_t) {
+ battery_monitor_.dumpState(fd);
+
+ ::android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);
+ HealthInfo health_info;
+ auto res = getHealthInfo(&health_info);
+ if (res.isOk()) {
+ ::android::base::WriteStringToFd(health_info.toString(), fd);
+ } else {
+ ::android::base::WriteStringToFd(res.getDescription(), fd);
+ }
+
+ fsync(fd);
+ return STATUS_OK;
+}
+
+std::optional<bool> Health::ShouldKeepScreenOn() {
+ if (!healthd_config_->screen_on) {
+ return std::nullopt;
+ }
+
+ HealthInfo health_info;
+ auto res = getHealthInfo(&health_info);
+ if (!res.isOk()) {
+ return std::nullopt;
+ }
+
+ ::android::BatteryProperties props = {};
+ convert(health_info, &props);
+ return healthd_config_->screen_on(&props);
+}
+
+namespace {
+bool IsDeadObjectLogged(const ndk::ScopedAStatus& ret) {
+ if (ret.isOk()) return false;
+ if (ret.getStatus() == ::STATUS_DEAD_OBJECT) return true;
+ LOG(ERROR) << "Cannot call healthInfoChanged on callback: " << ret.getDescription();
+ return false;
+}
+} // namespace
+
+//
+// Subclass helpers / overrides
+//
+
+void Health::UpdateHealthInfo(HealthInfo* /* health_info */) {
+ /*
+ // Sample code for a subclass to implement this:
+ // If you need to modify values (e.g. batteryChargeTimeToFullNowSeconds), do it here.
+ health_info->batteryChargeTimeToFullNowSeconds = calculate_charge_time_seconds();
+
+ // If you need to call healthd_board_battery_update, modify its signature
+ // and implementation to operate on HealthInfo directly, then call:
+ healthd_board_battery_update(health_info);
+ */
+}
+
+//
+// Methods that handle callbacks.
+//
+
+ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ // For now, this shouldn't happen because argument is not nullable.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+
+ {
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ callbacks_.emplace_back(LinkedCallback::Make(ref<Health>(), callback));
+ // unlock
+ }
+
+ HealthInfo health_info;
+ if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+ LOG(WARNING) << "Cannot call getHealthInfo: " << res.getDescription();
+ // No health info to send, so return early.
+ return ndk::ScopedAStatus::ok();
+ }
+
+ if (auto res = callback->healthInfoChanged(health_info); IsDeadObjectLogged(res)) {
+ (void)unregisterCallback(callback);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Health::unregisterCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ // For now, this shouldn't happen because argument is not nullable.
+ return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
+ }
+
+ std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
+
+ auto matches = [callback](const auto& linked) {
+ return linked->callback()->asBinder() == callback->asBinder(); // compares binder object
+ };
+ auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), matches);
+ bool removed = (it != callbacks_.end());
+ callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
+ return removed ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+}
+
+// A combination of the HIDL version
+// android::hardware::health::V2_1::implementation::Health::update() and
+// android::hardware::health::V2_1::implementation::BinderHealth::update()
+ndk::ScopedAStatus Health::update() {
+ HealthInfo health_info;
+ if (auto res = getHealthInfo(&health_info); !res.isOk()) {
+ LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
+ // Propagate service specific errors. If there's none, report unknown error.
+ if (res.getServiceSpecificError() != 0 ||
+ res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+ return res;
+ }
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
+ }
+ battery_monitor_.logValues();
+ OnHealthInfoChanged(health_info);
+ return ndk::ScopedAStatus::ok();
+}
+
+void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
+ // Notify all callbacks
+ std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
+ // is_dead notifies a callback and return true if it is dead.
+ auto is_dead = [&](const auto& linked) {
+ auto res = linked->callback()->healthInfoChanged(health_info);
+ return IsDeadObjectLogged(res);
+ };
+ auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
+ callbacks_.erase(it, callbacks_.end()); // calls unlinkToDeath on deleted callbacks.
+ lock.unlock();
+
+ // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
+}
+
+void Health::BinderEvent(uint32_t /*epevents*/) {
+ if (binder_fd_ >= 0) {
+ ABinderProcess_handlePolledCommands();
+ }
+}
+
+void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
+ LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";
+
+ // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
+ // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
+ *config = *healthd_config_.get();
+
+ binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);
+
+ if (status == ::STATUS_OK && binder_fd_ >= 0) {
+ std::shared_ptr<Health> thiz = ref<Health>();
+ auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
+ if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
+ PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
+ }
+ }
+
+ std::string health_name = IHealth::descriptor + "/"s + instance_name_;
+ CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
+ << instance_name_ << ": Failed to register HAL";
+
+ LOG(INFO) << instance_name_ << ": Hal init done";
+}
+
+// Unlike hwbinder, for binder, there's no need to explicitly call flushCommands()
+// in PrepareToWait(). See b/139697085.
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.cpp b/health/aidl/default/LinkedCallback.cpp
new file mode 100644
index 0000000..2985ffe
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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_ibinder.h>
+
+#include <health-impl/Health.h>
+#include <utils/Errors.h>
+
+#include "LinkedCallback.h"
+
+namespace aidl::android::hardware::health {
+
+std::unique_ptr<LinkedCallback> LinkedCallback::Make(
+ std::shared_ptr<Health> service, std::shared_ptr<IHealthInfoCallback> callback) {
+ std::unique_ptr<LinkedCallback> ret(new LinkedCallback());
+ binder_status_t linkRet =
+ AIBinder_linkToDeath(callback->asBinder().get(), service->death_recipient_.get(),
+ reinterpret_cast<void*>(ret.get()));
+ if (linkRet != ::STATUS_OK) {
+ LOG(WARNING) << __func__ << "Cannot link to death: " << linkRet;
+ return nullptr;
+ }
+ ret->service_ = service;
+ ret->callback_ = std::move(callback);
+ return ret;
+}
+
+LinkedCallback::LinkedCallback() = default;
+
+LinkedCallback::~LinkedCallback() {
+ if (callback_ == nullptr) {
+ return;
+ }
+ auto status =
+ AIBinder_unlinkToDeath(callback_->asBinder().get(), service()->death_recipient_.get(),
+ reinterpret_cast<void*>(this));
+ if (status != STATUS_OK && status != STATUS_DEAD_OBJECT) {
+ LOG(WARNING) << __func__ << "Cannot unlink to death: " << ::android::statusToString(status);
+ }
+}
+
+std::shared_ptr<Health> LinkedCallback::service() {
+ auto service_sp = service_.lock();
+ CHECK_NE(nullptr, service_sp);
+ return service_sp;
+}
+
+void LinkedCallback::OnCallbackDied() {
+ service()->unregisterCallback(callback_);
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/LinkedCallback.h b/health/aidl/default/LinkedCallback.h
new file mode 100644
index 0000000..82490a7
--- /dev/null
+++ b/health/aidl/default/LinkedCallback.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <memory>
+
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android-base/macros.h>
+#include <android/binder_auto_utils.h>
+
+#include <health-impl/Health.h>
+
+namespace aidl::android::hardware::health {
+
+// Type of the cookie pointer in linkToDeath.
+// A (Health, IHealthInfoCallback) tuple.
+class LinkedCallback {
+ public:
+ // Automatically linkToDeath upon construction with the returned object as the cookie.
+ // service->death_reciepient() should be from CreateDeathRecipient().
+ // Not using a strong reference to |service| to avoid circular reference. The lifetime
+ // of |service| must be longer than this LinkedCallback object.
+ static std::unique_ptr<LinkedCallback> Make(std::shared_ptr<Health> service,
+ std::shared_ptr<IHealthInfoCallback> callback);
+
+ // Automatically unlinkToDeath upon destruction. So, it is always safe to reinterpret_cast
+ // the cookie back to the LinkedCallback object.
+ ~LinkedCallback();
+
+ // The wrapped IHealthInfoCallback object.
+ const std::shared_ptr<IHealthInfoCallback>& callback() const { return callback_; }
+
+ // On callback died, unreigster it from the service.
+ void OnCallbackDied();
+
+ private:
+ LinkedCallback();
+ DISALLOW_COPY_AND_ASSIGN(LinkedCallback);
+
+ std::shared_ptr<Health> service();
+
+ std::weak_ptr<Health> service_;
+ std::shared_ptr<IHealthInfoCallback> callback_;
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/android.hardware.health-service.example.rc b/health/aidl/default/android.hardware.health-service.example.rc
new file mode 100644
index 0000000..b393c58
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.rc
@@ -0,0 +1,6 @@
+service vendor.health-default /vendor/bin/hw/android.hardware.health-service.example
+ class hal
+ user system
+ group system
+ capabilities WAKE_ALARM BLOCK_SUSPEND
+ file /dev/kmsg w
diff --git a/health/aidl/default/android.hardware.health-service.example.xml b/health/aidl/default/android.hardware.health-service.example.xml
new file mode 100644
index 0000000..98026cb
--- /dev/null
+++ b/health/aidl/default/android.hardware.health-service.example.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.health</name>
+ <version>1</version>
+ <fqname>IHealth/default</fqname>
+ </hal>
+</manifest>
diff --git a/health/aidl/default/health-convert.cpp b/health/aidl/default/health-convert.cpp
new file mode 100644
index 0000000..b5251f4
--- /dev/null
+++ b/health/aidl/default/health-convert.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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-convert.h"
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* p) {
+ p->chargerAcOnline = info.chargerAcOnline;
+ p->chargerUsbOnline = info.chargerUsbOnline;
+ p->chargerWirelessOnline = info.chargerWirelessOnline;
+ p->maxChargingCurrent = info.maxChargingCurrentMicroamps;
+ p->maxChargingVoltage = info.maxChargingVoltageMicrovolts;
+ p->batteryStatus = static_cast<int>(info.batteryStatus);
+ p->batteryHealth = static_cast<int>(info.batteryHealth);
+ p->batteryPresent = info.batteryPresent;
+ p->batteryLevel = info.batteryLevel;
+ p->batteryVoltage = info.batteryVoltageMillivolts;
+ p->batteryTemperature = info.batteryTemperatureTenthsCelsius;
+ p->batteryCurrent = info.batteryCurrentMicroamps;
+ p->batteryCycleCount = info.batteryCycleCount;
+ p->batteryFullCharge = info.batteryFullChargeUah;
+ p->batteryChargeCounter = info.batteryChargeCounterUah;
+ p->batteryTechnology = ::android::String8(info.batteryTechnology.c_str());
+}
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/health-convert.h b/health/aidl/default/health-convert.h
new file mode 100644
index 0000000..179ffc4
--- /dev/null
+++ b/health/aidl/default/health-convert.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 <aidl/android/hardware/health/HealthInfo.h>
+#include <batteryservice/BatteryService.h>
+#include <healthd/healthd.h>
+
+// Conversion between healthd types and AIDL health HAL types. Note that most
+// of the conversion loses information, because these types have a different
+// set of information.
+
+namespace aidl::android::hardware::health {
+
+void convert(const HealthInfo& info, struct ::android::BatteryProperties* out);
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/HalHealthLoop.h b/health/aidl/default/include/health-impl/HalHealthLoop.h
new file mode 100644
index 0000000..c46aaa4
--- /dev/null
+++ b/health/aidl/default/include/health-impl/HalHealthLoop.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/IHealth.h>
+#include <health/HealthLoop.h>
+
+namespace aidl::android::hardware::health {
+
+class HalHealthLoop;
+
+class HalHealthLoopCallback {
+ public:
+ virtual ~HalHealthLoopCallback() = default;
+
+ // Called by HalHealthLoop::Init
+ virtual void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) = 0;
+ // Called by HalHealthLoop::Heartbeat
+ virtual void OnHeartbeat(){};
+ // Called by HalHealthLoop::PrepareToWait
+ virtual int OnPrepareToWait() { return -1; }
+ // Called by HalHealthLoop::ScheduleBatteryUpdate
+ virtual void OnHealthInfoChanged(const HealthInfo&) {}
+};
+
+// AIDL version of android::hardware::health::V2_1::implementation::HalHealthLoop.
+// An implementation of HealthLoop for using a given health HAL.
+class HalHealthLoop final : public ::android::hardware::health::HealthLoop {
+ public:
+ // Caller must ensure that the lifetime of service_ is not shorter than this object.
+ HalHealthLoop(std::shared_ptr<IHealth> service, std::shared_ptr<HalHealthLoopCallback> callback)
+ : service_(std::move(service)), callback_(std::move(callback)) {}
+
+ using HealthLoop::RegisterEvent;
+
+ bool charger_online() const { return charger_online_; }
+
+ protected:
+ virtual void Init(struct healthd_config* config) override;
+ virtual void Heartbeat() override;
+ virtual int PrepareToWait() override;
+ virtual void ScheduleBatteryUpdate() override;
+
+ private:
+ std::shared_ptr<IHealth> service_;
+ std::shared_ptr<HalHealthLoopCallback> callback_;
+ bool charger_online_ = false;
+
+ // Helpers of OnHealthInfoChanged.
+ void set_charger_online(const HealthInfo& health_info);
+
+ // HealthLoop periodically calls ScheduleBatteryUpdate, which calls
+ // OnHealthInfoChanged callback. A subclass of the callback can override
+ // HalHealthLoopCallback::OnHealthInfoChanged to
+ // broadcast the health_info to interested listeners.
+ // This adjust uevents / wakealarm periods.
+ void OnHealthInfoChanged(const HealthInfo& health_info);
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/include/health-impl/Health.h b/health/aidl/default/include/health-impl/Health.h
new file mode 100644
index 0000000..e49f44c
--- /dev/null
+++ b/health/aidl/default/include/health-impl/Health.h
@@ -0,0 +1,113 @@
+/*
+ * 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 <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/BnHealth.h>
+#include <aidl/android/hardware/health/IHealthInfoCallback.h>
+#include <android/binder_auto_utils.h>
+#include <health-impl/HalHealthLoop.h>
+#include <healthd/BatteryMonitor.h>
+#include <healthd/healthd.h>
+
+namespace aidl::android::hardware::health {
+
+class LinkedCallback;
+
+// AIDL version of android::hardware::health::V2_1::implementation::Health and BinderHealth.
+// There's no need to separate the two in AIDL because AIDL does not support passthrough transport.
+//
+// Instead of inheriting from HalHealthLoop directly, implements the callback interface to
+// HalHealthLoop instead.
+//
+// Sample implementation of health HAL.
+class Health : public BnHealth, public HalHealthLoopCallback {
+ public:
+ // Initialize with |config|.
+ // A subclass may modify |config| before passing it to the parent constructor.
+ // See implementation of Health for code samples.
+ Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config);
+
+ ndk::ScopedAStatus registerCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) override;
+ ndk::ScopedAStatus unregisterCallback(
+ const std::shared_ptr<IHealthInfoCallback>& callback) override;
+ ndk::ScopedAStatus update() override;
+
+ // A subclass should not override this. Override UpdateHealthInfo instead.
+ ndk::ScopedAStatus getHealthInfo(HealthInfo* out) override final;
+
+ // A subclass is recommended to override the path in healthd_config in the constructor.
+ // Only override these if there are no suitable kernel interfaces to read these values.
+ ndk::ScopedAStatus getChargeCounterUah(int32_t* out) override;
+ ndk::ScopedAStatus getCurrentNowMicroamps(int32_t* out) override;
+ ndk::ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override;
+ ndk::ScopedAStatus getCapacity(int32_t* out) override;
+ ndk::ScopedAStatus getChargeStatus(BatteryStatus* out) override;
+
+ // A subclass may either override these or provide function pointers in
+ // in healthd_config in the constructor.
+ // Prefer overriding these for efficiency.
+ ndk::ScopedAStatus getEnergyCounterNwh(int64_t* out) override;
+
+ // A subclass may override these for a specific device.
+ // The default implementations return nothing in |out|.
+ ndk::ScopedAStatus getDiskStats(std::vector<DiskStats>* out) override;
+ ndk::ScopedAStatus getStorageInfo(std::vector<StorageInfo>* out) override;
+
+ // A subclass may override these to provide a different implementation.
+ binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
+
+ // HalHealthLoopCallback implementation.
+ void OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) override;
+ void OnHealthInfoChanged(const HealthInfo& health_info) override;
+
+ // A subclass may override this if it wants to handle binder events differently.
+ virtual void BinderEvent(uint32_t epevents);
+
+ // A subclass may override this to determine whether screen should be kept on in charger mode.
+ // Default is to invoke healthd_config->screen_on() on the BatteryProperties converted
+ // from getHealthInfo.
+ // Prefer overriding these to providing screen_on in healthd_config in the constructor
+ // for efficiency.
+ virtual std::optional<bool> ShouldKeepScreenOn();
+
+ protected:
+ // A subclass can override this to modify any health info object before
+ // returning to clients. This is similar to healthd_board_battery_update().
+ // By default, it does nothing.
+ // See implementation of Health for code samples.
+ virtual void UpdateHealthInfo(HealthInfo* health_info);
+
+ private:
+ friend LinkedCallback; // for exposing death_recipient_
+
+ bool unregisterCallbackInternal(std::shared_ptr<IHealthInfoCallback> callback);
+
+ std::string instance_name_;
+ ::android::BatteryMonitor battery_monitor_;
+ std::unique_ptr<struct healthd_config> healthd_config_;
+
+ ndk::ScopedAIBinder_DeathRecipient death_recipient_;
+ int binder_fd_ = -1;
+ std::mutex callbacks_lock_;
+ std::vector<std::unique_ptr<LinkedCallback>> callbacks_;
+};
+
+} // namespace aidl::android::hardware::health
diff --git a/health/aidl/default/main.cpp b/health/aidl/default/main.cpp
new file mode 100644
index 0000000..014ae34
--- /dev/null
+++ b/health/aidl/default/main.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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_interface_utils.h>
+#include <health-impl/Health.h>
+#include <health/utils.h>
+
+using aidl::android::hardware::health::HalHealthLoop;
+using aidl::android::hardware::health::Health;
+
+static constexpr const char* gInstanceName = "default";
+
+int main() {
+ // TODO(b/203246116): handle charger
+ // make a default health service
+ auto config = std::make_unique<healthd_config>();
+ ::android::hardware::health::InitHealthdConfig(config.get());
+ auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config));
+ auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
+ return hal_health_loop->StartLoop();
+}
diff --git a/health/aidl/include/android/hardware/health/translate-ndk.h b/health/aidl/include/android/hardware/health/translate-ndk.h
new file mode 100644
index 0000000..2f8fe04
--- /dev/null
+++ b/health/aidl/include/android/hardware/health/translate-ndk.h
@@ -0,0 +1,39 @@
+/*
+ * 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/BatteryCapacityLevel.h>
+#include <aidl/android/hardware/health/DiskStats.h>
+#include <aidl/android/hardware/health/HealthInfo.h>
+#include <aidl/android/hardware/health/StorageInfo.h>
+#include <android/hardware/health/2.0/types.h>
+#include <android/hardware/health/2.1/types.h>
+#include <limits>
+
+namespace android::h2a {
+
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_0::StorageInfo& in,
+ aidl::android::hardware::health::StorageInfo* out);
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_0::DiskStats& in,
+ aidl::android::hardware::health::DiskStats* out);
+__attribute__((warn_unused_result)) bool translate(
+ const ::android::hardware::health::V2_1::HealthInfo& in,
+ aidl::android::hardware::health::HealthInfo* out);
+
+} // namespace android::h2a
diff --git a/health/aidl/vts/functional/Android.bp b/health/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..434f565
--- /dev/null
+++ b/health/aidl/vts/functional/Android.bp
@@ -0,0 +1,48 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+ name: "VtsHalHealthTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsHalHealthTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.health-V1-ndk",
+ "libgmock",
+ ],
+ header_libs: [
+ "libhealthtest_headers",
+ ],
+ test_suites: [
+ "vts",
+ ],
+}
diff --git a/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
new file mode 100644
index 0000000..3e07188
--- /dev/null
+++ b/health/aidl/vts/functional/VtsHalHealthTargetTest.cpp
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "health_aidl_hal_test"
+
+#include <chrono>
+#include <memory>
+#include <thread>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/health/BnHealthInfoCallback.h>
+#include <aidl/android/hardware/health/IHealth.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_enums.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <health-test/TestUtils.h>
+
+using android::getAidlHalInstanceNames;
+using android::PrintInstanceNameToString;
+using android::hardware::health::test_utils::SucceedOnce;
+using ndk::enum_range;
+using ndk::ScopedAStatus;
+using ndk::SharedRefBase;
+using ndk::SpAIBinder;
+using testing::AllOf;
+using testing::AnyOf;
+using testing::AnyOfArray;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+using testing::Contains;
+using testing::Each;
+using testing::Eq;
+using testing::ExplainMatchResult;
+using testing::Ge;
+using testing::Gt;
+using testing::Le;
+using testing::Lt;
+using testing::Matcher;
+using testing::Not;
+using namespace std::string_literals;
+using namespace std::chrono_literals;
+
+namespace aidl::android::hardware::health {
+
+static constexpr int32_t kFullChargeDesignCapMinUah = 100 * 1000;
+static constexpr int32_t kFullChargeDesignCapMaxUah = 100 * 1000 * 1000;
+
+MATCHER(IsOk, "") {
+ *result_listener << "status is " << arg.getDescription();
+ return arg.isOk();
+}
+
+MATCHER_P(ExceptionIs, exception_code, "") {
+ *result_listener << "status is " << arg.getDescription();
+ return arg.getExceptionCode() == exception_code;
+}
+
+template <typename T>
+Matcher<T> InClosedRange(const T& lo, const T& hi) {
+ return AllOf(Ge(lo), Le(hi));
+}
+
+template <typename T>
+Matcher<T> IsValidEnum() {
+ return AnyOfArray(enum_range<T>().begin(), enum_range<T>().end());
+}
+
+class HealthAidl : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ health = IHealth::fromBinder(binder);
+ ASSERT_NE(health, nullptr);
+ }
+ std::shared_ptr<IHealth> health;
+};
+
+class Callback : public BnHealthInfoCallback {
+ public:
+ ScopedAStatus healthInfoChanged(const HealthInfo&) override {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ invoked_ = true;
+ }
+ invoked_notify_.notify_all();
+ return ScopedAStatus::ok();
+ }
+ template <typename R, typename P>
+ [[nodiscard]] bool waitInvoke(std::chrono::duration<R, P> duration) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ bool r = invoked_notify_.wait_for(lock, duration, [this] { return this->invoked_; });
+ invoked_ = false;
+ return r;
+ }
+
+ private:
+ std::mutex mutex_;
+ std::condition_variable invoked_notify_;
+ bool invoked_ = false;
+};
+
+TEST_P(HealthAidl, Callbacks) {
+ auto first_callback = SharedRefBase::make<Callback>();
+ auto second_callback = SharedRefBase::make<Callback>();
+
+ ASSERT_THAT(health->registerCallback(first_callback), IsOk());
+ ASSERT_THAT(health->registerCallback(second_callback), IsOk());
+
+ // registerCallback may or may not invoke the callback immediately, so the test needs
+ // to wait for the invocation. If the implementation chooses not to invoke the callback
+ // immediately, just wait for some time.
+ (void)first_callback->waitInvoke(200ms);
+ (void)second_callback->waitInvoke(200ms);
+
+ // assert that the first callback is invoked when update is called.
+ ASSERT_THAT(health->update(), IsOk());
+
+ ASSERT_TRUE(first_callback->waitInvoke(1s));
+ ASSERT_TRUE(second_callback->waitInvoke(1s));
+
+ ASSERT_THAT(health->unregisterCallback(first_callback), IsOk());
+
+ // clear any potentially pending callbacks result from wakealarm / kernel events
+ // If there is none, just wait for some time.
+ (void)first_callback->waitInvoke(200ms);
+ (void)second_callback->waitInvoke(200ms);
+
+ // assert that the second callback is still invoked even though the first is unregistered.
+ ASSERT_THAT(health->update(), IsOk());
+
+ ASSERT_FALSE(first_callback->waitInvoke(200ms));
+ ASSERT_TRUE(second_callback->waitInvoke(1s));
+
+ ASSERT_THAT(health->unregisterCallback(second_callback), IsOk());
+}
+
+TEST_P(HealthAidl, UnregisterNonExistentCallback) {
+ auto callback = SharedRefBase::make<Callback>();
+ auto ret = health->unregisterCallback(callback);
+ ASSERT_THAT(ret, ExceptionIs(EX_ILLEGAL_ARGUMENT));
+}
+
+/*
+ * Tests the values returned by getChargeCounterUah() from interface IHealth.
+ */
+TEST_P(HealthAidl, getChargeCounterUah) {
+ int32_t value;
+ auto status = health->getChargeCounterUah(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, Ge(0));
+}
+
+/*
+ * Tests the values returned by getCurrentNowMicroamps() from interface IHealth.
+ */
+TEST_P(HealthAidl, getCurrentNowMicroamps) {
+ int32_t value;
+ auto status = health->getCurrentNowMicroamps(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, Not(INT32_MIN));
+}
+
+/*
+ * Tests the values returned by getCurrentAverageMicroamps() from interface IHealth.
+ */
+TEST_P(HealthAidl, getCurrentAverageMicroamps) {
+ int32_t value;
+ auto status = health->getCurrentAverageMicroamps(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, Not(INT32_MIN));
+}
+
+/*
+ * Tests the values returned by getCapacity() from interface IHealth.
+ */
+TEST_P(HealthAidl, getCapacity) {
+ int32_t value;
+ auto status = health->getCapacity(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, InClosedRange(0, 100));
+}
+
+/*
+ * Tests the values returned by getEnergyCounterNwh() from interface IHealth.
+ */
+TEST_P(HealthAidl, getEnergyCounterNwh) {
+ int64_t value;
+ auto status = health->getEnergyCounterNwh(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, Not(INT64_MIN));
+}
+
+/*
+ * Tests the values returned by getChargeStatus() from interface IHealth.
+ */
+TEST_P(HealthAidl, getChargeStatus) {
+ BatteryStatus value;
+ auto status = health->getChargeStatus(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, IsValidEnum<BatteryStatus>());
+}
+
+MATCHER(IsValidStorageInfo, "") {
+ *result_listener << "value is " << arg.toString() << ".";
+ if (!ExplainMatchResult(InClosedRange(0, 3), arg.eol, result_listener)) {
+ *result_listener << " for eol.";
+ return false;
+ }
+ if (!ExplainMatchResult(InClosedRange(0, 0x0B), arg.lifetimeA, result_listener)) {
+ *result_listener << " for lifetimeA.";
+ return false;
+ }
+ if (!ExplainMatchResult(InClosedRange(0, 0x0B), arg.lifetimeB, result_listener)) {
+ *result_listener << " for lifetimeB.";
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Tests the values returned by getStorageInfo() from interface IHealth.
+ */
+TEST_P(HealthAidl, getStorageInfo) {
+ std::vector<StorageInfo> value;
+ auto status = health->getStorageInfo(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, Each(IsValidStorageInfo()));
+}
+
+/*
+ * Tests the values returned by getDiskStats() from interface IHealth.
+ */
+TEST_P(HealthAidl, getDiskStats) {
+ std::vector<DiskStats> value;
+ auto status = health->getDiskStats(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+}
+
+MATCHER(IsValidHealthInfo, "") {
+ *result_listener << "value is " << arg.toString() << ".";
+ if (!ExplainMatchResult(Each(IsValidStorageInfo()), arg.storageInfos, result_listener)) {
+ *result_listener << " for storageInfos.";
+ return false;
+ }
+
+ if (!ExplainMatchResult(Not(INT32_MIN), arg.batteryCurrentMicroamps, result_listener)) {
+ *result_listener << " for batteryCurrentMicroamps.";
+ return false;
+ }
+
+ if (!ExplainMatchResult(InClosedRange(0, 100), arg.batteryLevel, result_listener)) {
+ *result_listener << " for batteryLevel.";
+ return false;
+ }
+
+ if (!ExplainMatchResult(IsValidEnum<BatteryHealth>(), arg.batteryHealth, result_listener)) {
+ *result_listener << " for batteryHealth.";
+ return false;
+ }
+
+ if (!ExplainMatchResult(IsValidEnum<BatteryStatus>(), arg.batteryStatus, result_listener)) {
+ *result_listener << " for batteryStatus.";
+ return false;
+ }
+
+ if (arg.batteryPresent) {
+ if (!ExplainMatchResult(Gt(0), arg.batteryChargeCounterUah, result_listener)) {
+ *result_listener << " for batteryChargeCounterUah when battery is present.";
+ return false;
+ }
+ if (!ExplainMatchResult(Not(BatteryStatus::UNKNOWN), arg.batteryStatus, result_listener)) {
+ *result_listener << " for batteryStatus when battery is present.";
+ return false;
+ }
+ }
+
+ if (!ExplainMatchResult(IsValidEnum<BatteryCapacityLevel>(), arg.batteryCapacityLevel,
+ result_listener)) {
+ *result_listener << " for batteryCapacityLevel.";
+ return false;
+ }
+ if (!ExplainMatchResult(Ge(-1), arg.batteryChargeTimeToFullNowSeconds, result_listener)) {
+ *result_listener << " for batteryChargeTimeToFullNowSeconds.";
+ return false;
+ }
+
+ if (!ExplainMatchResult(
+ AnyOf(Eq(0), AllOf(Gt(kFullChargeDesignCapMinUah), Lt(kFullChargeDesignCapMaxUah))),
+ arg.batteryFullChargeDesignCapacityUah, result_listener)) {
+ *result_listener << " for batteryFullChargeDesignCapacityUah. It should be greater than "
+ "100 mAh and less than 100,000 mAh, or 0 if unknown";
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Tests the values returned by getHealthInfo() from interface IHealth.
+ */
+TEST_P(HealthAidl, getHealthInfo) {
+ HealthInfo value;
+ auto status = health->getHealthInfo(&value);
+ ASSERT_THAT(status, AnyOf(IsOk(), ExceptionIs(EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+ ASSERT_THAT(value, IsValidHealthInfo());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthAidl);
+INSTANTIATE_TEST_SUITE_P(Health, HealthAidl,
+ testing::ValuesIn(getAidlHalInstanceNames(IHealth::descriptor)),
+ PrintInstanceNameToString);
+
+// For battery current tests, value may not be stable if the battery current has fluctuated.
+// Retry in a bit more time (with the following timeout) and consider the test successful if it
+// has succeed once.
+static constexpr auto gBatteryTestTimeout = 1min;
+static constexpr double gCurrentCompareFactor = 0.50;
+class BatteryTest : public HealthAidl {};
+
+// Tuple for all IHealth::get* API return values.
+template <typename T>
+struct HalResult {
+ std::shared_ptr<ScopedAStatus> result = std::make_shared<ScopedAStatus>();
+ T value;
+};
+
+// Needs to be called repeatedly within a period of time to ensure values are initialized.
+static AssertionResult IsBatteryCurrentSignCorrect(const HalResult<BatteryStatus>& status,
+ const HalResult<int32_t>& current,
+ bool acceptZeroCurrentAsUnknown) {
+ // getChargeStatus / getCurrentNow / getCurrentAverage / getHealthInfo already tested above.
+ // Here, just skip if not ok.
+ if (!status.result->isOk()) {
+ return AssertionSuccess() << "getChargeStatus / getHealthInfo returned "
+ << status.result->getDescription() << ", skipping";
+ }
+
+ if (!current.result->isOk()) {
+ return AssertionSuccess() << "getCurrentNow / getCurrentAverage returned "
+ << current.result->getDescription() << ", skipping";
+ }
+
+ return ::android::hardware::health::test_utils::IsBatteryCurrentSignCorrect(
+ status.value, current.value, acceptZeroCurrentAsUnknown,
+ [](BatteryStatus status) { return toString(status); });
+}
+
+static AssertionResult IsBatteryCurrentSimilar(const HalResult<BatteryStatus>& status,
+ const HalResult<int32_t>& current_now,
+ const HalResult<int32_t>& current_average) {
+ if (status.result->isOk() && status.value == BatteryStatus::FULL) {
+ // No reason to test on full battery because battery current load fluctuates.
+ return AssertionSuccess() << "Battery is full, skipping";
+ }
+
+ // getCurrentNow / getCurrentAverage / getHealthInfo already tested above. Here, just skip if
+ // not SUCCESS or value 0.
+ if (!current_now.result->isOk() || current_now.value == 0) {
+ return AssertionSuccess() << "getCurrentNow returned "
+ << current_now.result->getDescription() << " with value "
+ << current_now.value << ", skipping";
+ }
+
+ if (!current_average.result->isOk() || current_average.value == 0) {
+ return AssertionSuccess() << "getCurrentAverage returned "
+ << current_average.result->getDescription() << " with value "
+ << current_average.value << ", skipping";
+ }
+
+ return ::android::hardware::health::test_utils::IsBatteryCurrentSimilar(
+ current_now.value, current_average.value, gCurrentCompareFactor);
+}
+
+TEST_P(BatteryTest, InstantCurrentAgainstChargeStatusInHealthInfo) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<HealthInfo> health_info;
+ *health_info.result = health->getHealthInfo(&health_info.value);
+
+ return IsBatteryCurrentSignCorrect(
+ {health_info.result, health_info.value.batteryStatus},
+ {health_info.result, health_info.value.batteryCurrentMicroamps},
+ true /* accept zero current as unknown */);
+ };
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_now becomes stable.";
+}
+
+TEST_P(BatteryTest, AverageCurrentAgainstChargeStatusInHealthInfo) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<HealthInfo> health_info;
+ *health_info.result = health->getHealthInfo(&health_info.value);
+ return IsBatteryCurrentSignCorrect(
+ {health_info.result, health_info.value.batteryStatus},
+ {health_info.result, health_info.value.batteryCurrentAverageMicroamps},
+ true /* accept zero current as unknown */);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_average becomes stable.";
+}
+
+TEST_P(BatteryTest, InstantCurrentAgainstAverageCurrentInHealthInfo) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<HealthInfo> health_info;
+ *health_info.result = health->getHealthInfo(&health_info.value);
+ return IsBatteryCurrentSimilar(
+ {health_info.result, health_info.value.batteryStatus},
+ {health_info.result, health_info.value.batteryCurrentMicroamps},
+ {health_info.result, health_info.value.batteryCurrentAverageMicroamps});
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_now and current_average becomes "
+ "stable.";
+}
+
+TEST_P(BatteryTest, InstantCurrentAgainstChargeStatusFromHal) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<BatteryStatus> status;
+ *status.result = health->getChargeStatus(&status.value);
+ HalResult<int32_t> current_now;
+ *current_now.result = health->getCurrentNowMicroamps(¤t_now.value);
+ return IsBatteryCurrentSignCorrect(status, current_now,
+ false /* accept zero current as unknown */);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_now becomes stable.";
+}
+
+TEST_P(BatteryTest, AverageCurrentAgainstChargeStatusFromHal) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<BatteryStatus> status;
+ *status.result = health->getChargeStatus(&status.value);
+ HalResult<int32_t> current_average;
+ *current_average.result = health->getCurrentAverageMicroamps(¤t_average.value);
+ return IsBatteryCurrentSignCorrect(status, current_average,
+ false /* accept zero current as unknown */);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_average becomes stable.";
+}
+
+TEST_P(BatteryTest, InstantCurrentAgainstAverageCurrentFromHal) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<BatteryStatus> status;
+ *status.result = health->getChargeStatus(&status.value);
+ HalResult<int32_t> current_now;
+ *current_now.result = health->getCurrentNowMicroamps(¤t_now.value);
+ HalResult<int32_t> current_average;
+ *current_average.result = health->getCurrentAverageMicroamps(¤t_average.value);
+ return IsBatteryCurrentSimilar(status, current_now, current_average);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when current_average becomes stable.";
+}
+
+AssertionResult IsBatteryStatusCorrect(const HalResult<BatteryStatus>& status,
+ const HalResult<HealthInfo>& health_info) {
+ // getChargetStatus / getHealthInfo is already tested above. Here, just skip if not ok.
+ if (!health_info.result->isOk()) {
+ return AssertionSuccess() << "getHealthInfo returned "
+ << health_info.result->getDescription() << ", skipping";
+ }
+ if (!status.result->isOk()) {
+ return AssertionSuccess() << "getChargeStatus returned " << status.result->getDescription()
+ << ", skipping";
+ }
+ return ::android::hardware::health::test_utils::IsBatteryStatusCorrect(
+ status.value, health_info.value, [](BatteryStatus status) { return toString(status); });
+}
+
+TEST_P(BatteryTest, ConnectedAgainstStatusFromHal) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<BatteryStatus> status;
+ *status.result = health->getChargeStatus(&status.value);
+ HalResult<HealthInfo> health_info;
+ *health_info.result = health->getHealthInfo(&health_info.value);
+ return IsBatteryStatusCorrect(status, health_info);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when battery_status becomes stable.";
+}
+
+TEST_P(BatteryTest, ConnectedAgainstStatusInHealthInfo) {
+ auto testOnce = [&]() -> AssertionResult {
+ HalResult<HealthInfo> health_info;
+ *health_info.result = health->getHealthInfo(&health_info.value);
+ return IsBatteryStatusCorrect({health_info.result, health_info.value.batteryStatus},
+ health_info);
+ };
+
+ EXPECT_TRUE(SucceedOnce(gBatteryTestTimeout, testOnce))
+ << "You may want to try again later when getHealthInfo becomes stable.";
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BatteryTest);
+INSTANTIATE_TEST_SUITE_P(Health, BatteryTest,
+ testing::ValuesIn(getAidlHalInstanceNames(IHealth::descriptor)),
+ PrintInstanceNameToString);
+
+} // namespace aidl::android::hardware::health
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index 693e6cb..54b2740 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -46,6 +46,12 @@
// Init is called right after epollfd_ is initialized (so RegisterEvent
// is allowed) but before other things are initialized (so SetChargerOnline
// is not allowed.)
+ // The implementation of Init() should pull configuration from the
+ // underlying health HAL (via getHealthConfig()), and store it into
+ // |config|. The implementation may not initialize:
+ // - screen_on, because charger calls getScreenOn() from the HAL directly
+ // - ignorePowerSupplyNames, because it isn't used by any clients of the
+ // health HAL.
virtual void Init(healthd_config* config) = 0;
virtual void Heartbeat() = 0;
virtual int PrepareToWait() = 0;
diff --git a/health/utils/libhealthtest/Android.bp b/health/utils/libhealthtest/Android.bp
new file mode 100644
index 0000000..0993cb6
--- /dev/null
+++ b/health/utils/libhealthtest/Android.bp
@@ -0,0 +1,38 @@
+// 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.
+
+// Utils library for VTS tests.
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_headers {
+ name: "libhealthtest_headers",
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ export_static_lib_headers: [
+ "libgmock",
+ "libgtest",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/health/utils/libhealthtest/include/health-test/TestUtils.h b/health/utils/libhealthtest/include/health-test/TestUtils.h
new file mode 100644
index 0000000..e69c411
--- /dev/null
+++ b/health/utils/libhealthtest/include/health-test/TestUtils.h
@@ -0,0 +1,149 @@
+#pragma once
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+namespace android::hardware::health::test_utils {
+
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+using std::chrono_literals::operator""s;
+
+// Needs to be called repeatedly within a period of time to ensure values are initialized.
+template <typename BatteryStatusType, typename BatteryStatusToStringFn>
+inline AssertionResult IsBatteryCurrentSignCorrect(const BatteryStatusType& status, int32_t current,
+ bool acceptZeroCurrentAsUnknown,
+ const BatteryStatusToStringFn& toString) {
+ // For IHealth.getCurrentNow/Average, if current is not available, it is expected that
+ // the error code is NOT_SUPPORTED, which is checked above. Hence, zero current is
+ // not treated as unknown values.
+ // For IHealth.getHealthInfo, if current is not available, health_info.current_* == 0.
+ // Caller of this function provides current.result == Result::SUCCESS. Hence, just skip the
+ // check.
+ if (current == 0 && acceptZeroCurrentAsUnknown) {
+ return AssertionSuccess()
+ << "current is 0, which indicates the value may not be available. Skipping.";
+ }
+
+ switch (status) {
+ case BatteryStatusType::UNKNOWN:
+ if (current != 0) {
+ // BatteryStatus may be UNKNOWN initially with a non-zero current value, but
+ // after it is initialized, it should be known.
+ return AssertionFailure()
+ << "BatteryStatus is UNKNOWN but current is not 0. Actual: " << current;
+ }
+ break;
+ case BatteryStatusType::CHARGING:
+ if (current <= 0) {
+ return AssertionFailure()
+ << "BatteryStatus is CHARGING but current is not positive. Actual: "
+ << current;
+ }
+ break;
+ case BatteryStatusType::NOT_CHARGING:
+ if (current > 0) {
+ return AssertionFailure() << "BatteryStatus is " << toString(status)
+ << " but current is positive. Actual: " << current;
+ }
+ break;
+ case BatteryStatusType::DISCHARGING:
+ if (current >= 0) {
+ return AssertionFailure() << "BatteryStatus is " << toString(status)
+ << " but current is not negative. Actual: " << current;
+ }
+ break;
+ case BatteryStatusType::FULL:
+ // Battery current may be positive or negative depending on the load.
+ break;
+ default:
+ return AssertionFailure() << "Unknown BatteryStatus " << toString(status);
+ }
+
+ return AssertionSuccess() << "BatteryStatus is " << toString(status)
+ << " and current has the correct sign: " << current;
+}
+
+inline AssertionResult IsValueSimilar(int32_t dividend, int32_t divisor, double factor) {
+ auto difference = abs(dividend - divisor);
+ if (difference > factor * abs(divisor)) {
+ return AssertionFailure() << dividend << " and " << divisor
+ << " are not similar (factor = " << factor << ")";
+ }
+ return AssertionSuccess() << dividend << " and " << divisor
+ << " are similar (factor = " << factor << ")";
+}
+
+inline AssertionResult IsBatteryCurrentSimilar(int32_t currentNow, int32_t currentAverage,
+ double currentCompareFactor) {
+ // Check that the two values are similar. Note that the two tests uses a different
+ // divisor to ensure that they are actually pretty similar. For example,
+ // IsValueSimilar(5,10,0.4) returns true, but IsValueSimlar(10,5,0.4) returns false.
+ auto res = IsValueSimilar(currentNow, currentAverage, currentCompareFactor)
+ << " for now vs. average. Check units.";
+ if (!res) return res;
+ res = IsValueSimilar(currentAverage, currentNow, currentCompareFactor)
+ << " for average vs. now. Check units.";
+ if (!res) return res;
+ return AssertionSuccess() << "currentNow = " << currentNow
+ << " and currentAverage = " << currentAverage
+ << " are considered similar.";
+}
+
+// Test that f() returns AssertionSuccess() once in a given period of time.
+template <typename Duration, typename Function>
+inline AssertionResult SucceedOnce(Duration d, Function f) {
+ AssertionResult result = AssertionFailure() << "Function is never evaluated.";
+ auto end = std::chrono::system_clock::now() + d;
+ while (std::chrono::system_clock::now() <= end) {
+ result = f();
+ if (result) {
+ return result;
+ }
+ std::this_thread::sleep_for(2s);
+ }
+ return result;
+}
+
+template <typename BatteryStatusType, typename BatteryInfoType, typename BatteryStatusToStringFn>
+AssertionResult IsBatteryStatusCorrect(const BatteryStatusType& status,
+ const BatteryInfoType& batteryInfo,
+ const BatteryStatusToStringFn& toString) {
+ bool isConnected = batteryInfo.chargerAcOnline || batteryInfo.chargerUsbOnline ||
+ batteryInfo.chargerWirelessOnline;
+
+ std::stringstream message;
+ message << "BatteryStatus is " << toString(status) << " and " << (isConnected ? "" : "no ")
+ << "power source is connected: ac=" << batteryInfo.chargerAcOnline
+ << ", usb=" << batteryInfo.chargerUsbOnline
+ << ", wireless=" << batteryInfo.chargerWirelessOnline;
+
+ switch (status) {
+ case BatteryStatusType::UNKNOWN: {
+ // Don't enforce anything on isConnected on unknown battery status.
+ // Battery-less devices must report UNKNOWN battery status, but may report true
+ // or false on isConnected.
+ } break;
+ case BatteryStatusType::CHARGING:
+ case BatteryStatusType::NOT_CHARGING:
+ case BatteryStatusType::FULL: {
+ if (!isConnected) {
+ return AssertionFailure() << message.str();
+ }
+ } break;
+ case BatteryStatusType::DISCHARGING: {
+ if (isConnected) {
+ return AssertionFailure() << message.str();
+ }
+ } break;
+ default: {
+ return AssertionFailure() << "Unknown battery status value " << toString(status);
+ } break;
+ }
+
+ return AssertionSuccess() << message.str();
+}
+
+} // namespace android::hardware::health::test_utils
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
index 8c51c67..31cdded 100644
--- a/neuralnetworks/1.0/utils/Android.bp
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -31,6 +31,7 @@
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
static_libs: [
+ "libarect",
"neuralnetworks_types",
"neuralnetworks_utils_hal_common",
],
@@ -40,6 +41,11 @@
export_static_lib_headers: [
"neuralnetworks_utils_hal_common",
],
+ target: {
+ android: {
+ shared_libs: ["libnativewindow"],
+ },
+ },
}
cc_test {
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
index 3b32e1d..1ab9dcb 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -24,9 +24,10 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
+#include "nnapi/hal/1.0/ProtectCallback.h"
+
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
// lifetimes across processes and for protecting asynchronous calls across HIDL.
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
index 5d4bdbc..a770d06 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -36,6 +36,7 @@
GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
GeneralResult<Model::OperandValues> unvalidatedConvert(
const hardware::hidl_vec<uint8_t>& operandValues);
+GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
GeneralResult<SharedMemory> unvalidatedConvert(const hardware::hidl_memory& memory);
GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
GeneralResult<Request::Argument> unvalidatedConvert(
@@ -65,6 +66,7 @@
nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory);
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
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..0a6ca3e 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
@@ -24,7 +24,8 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
+
+#include "nnapi/hal/1.0/ProtectCallback.h"
#include <functional>
#include <memory>
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
index e201e25..66497c2 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
@@ -22,9 +22,9 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include "PreparedModel.h"
+#include "nnapi/hal/1.0/ProtectCallback.h"
#include <memory>
#include <utility>
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
similarity index 79%
rename from neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
rename to neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
index e51f916..8e02633 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/HandleError.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/HandleError.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
#include <android/hidl/base/1.0/IBase.h>
#include <hidl/HidlSupport.h>
@@ -27,7 +27,7 @@
namespace android::hardware::neuralnetworks::utils {
template <typename Type>
-nn::GeneralResult<Type> handleTransportError(const hardware::Return<Type>& ret) {
+nn::GeneralResult<Type> handleTransportError(const Return<Type>& ret) {
if (ret.isDeadObject()) {
return nn::error(nn::ErrorStatus::DEAD_OBJECT)
<< "Return<>::isDeadObject returned true: " << ret.description();
@@ -52,13 +52,13 @@
std::move(result).value(); \
})
-#define HANDLE_HAL_STATUS(status) \
- if (const auto canonical = ::android::nn::convert(status).value_or( \
- ::android::nn::ErrorStatus::GENERAL_FAILURE); \
- canonical == ::android::nn::ErrorStatus::NONE) { \
- } else \
+#define HANDLE_STATUS_HIDL(status) \
+ if (const ::android::nn::ErrorStatus canonical = ::android::nn::convert(status).value_or( \
+ ::android::nn::ErrorStatus::GENERAL_FAILURE); \
+ canonical == ::android::nn::ErrorStatus::NONE) { \
+ } else \
return NN_ERROR(canonical)
} // namespace android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_HANDLE_ERROR_H
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 48be595..bdb5b54 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
@@ -22,7 +22,8 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
+
+#include "nnapi/hal/1.0/ProtectCallback.h"
#include <memory>
#include <tuple>
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
similarity index 93%
rename from neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
rename to neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
index 05110bc..7418cfa 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/ProtectCallback.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
#include <android-base/scopeguard.h>
#include <android-base/thread_annotations.h>
@@ -98,4 +98,4 @@
} // namespace android::hardware::neuralnetworks::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_PROTECT_CALLBACK_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PROTECT_CALLBACK_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
index 360b338..5c1480e 100644
--- a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -25,7 +25,6 @@
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
-#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_0::utils {
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
index ea3ea56..7b478ae 100644
--- a/neuralnetworks/1.0/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -17,7 +17,9 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "HandleError.h"
#include "PreparedModel.h"
+#include "ProtectCallback.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
@@ -27,8 +29,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <utility>
@@ -40,19 +40,19 @@
nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
- HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "get supported operations failed with " << toString(status);
return supportedOperations;
}
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return NN_TRY(PreparedModel::create(preparedModel));
}
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
ErrorStatus status) {
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
return {};
}
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
index c0498eb..daa10fd 100644
--- a/neuralnetworks/1.0/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -37,6 +37,11 @@
#include "Utils.h"
+#ifdef __ANDROID__
+#include <android/hardware_buffer.h>
+#include <vndk/hardware_buffer.h>
+#endif // __ANDROID__
+
namespace {
template <typename Type>
@@ -49,6 +54,7 @@
namespace android::nn {
namespace {
+using hardware::hidl_handle;
using hardware::hidl_memory;
using hardware::hidl_vec;
@@ -74,6 +80,121 @@
return canonical;
}
+nn::GeneralResult<nn::Memory::Unknown::Handle> unknownHandleFromNativeHandle(
+ const native_handle_t* handle) {
+ if (handle == nullptr) {
+ return NN_ERROR() << "unknownHandleFromNativeHandle failed because handle is nullptr";
+ }
+
+ std::vector<base::unique_fd> fds =
+ NN_TRY(nn::dupFds(handle->data + 0, handle->data + handle->numFds));
+
+ std::vector<int> ints(handle->data + handle->numFds,
+ handle->data + handle->numFds + handle->numInts);
+
+ return nn::Memory::Unknown::Handle{.fds = std::move(fds), .ints = std::move(ints)};
+}
+
+nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
+ CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
+ if (!memory.valid()) {
+ return NN_ERROR() << "Unable to convert invalid hidl_memory";
+ }
+
+ if (memory.name() == "ashmem") {
+ if (memory.handle()->numFds != 1) {
+ return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+ << memory.handle()->numFds << " numFds, but expected 1";
+ }
+ if (memory.handle()->numInts != 0) {
+ return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+ << memory.handle()->numInts << " numInts, but expected 0";
+ }
+ auto handle = nn::Memory::Ashmem{
+ .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
+ .size = static_cast<size_t>(memory.size()),
+ };
+ return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+ }
+
+ if (memory.name() == "mmap_fd") {
+ if (memory.handle()->numFds != 1) {
+ return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+ << memory.handle()->numFds << " numFds, but expected 1";
+ }
+ if (memory.handle()->numInts != 3) {
+ return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+ << memory.handle()->numInts << " numInts, but expected 3";
+ }
+
+ const int fd = memory.handle()->data[0];
+ const int prot = memory.handle()->data[1];
+ const int lower = memory.handle()->data[2];
+ const int higher = memory.handle()->data[3];
+ const size_t offset = nn::getOffsetFromInts(lower, higher);
+
+ return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
+ }
+
+ if (memory.name() != "hardware_buffer_blob") {
+ auto handle = nn::Memory::Unknown{
+ .handle = NN_TRY(unknownHandleFromNativeHandle(memory.handle())),
+ .size = static_cast<size_t>(memory.size()),
+ .name = memory.name(),
+ };
+ return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+ }
+
+#ifdef __ANDROID__
+ constexpr auto roundUpToMultiple = [](uint32_t value, uint32_t multiple) -> uint32_t {
+ return (value + multiple - 1) / multiple * multiple;
+ };
+
+ const auto size = memory.size();
+ const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
+ const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ const uint32_t width = size;
+ const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
+ const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
+
+ // AHardwareBuffer_createFromHandle() might fail because an allocator
+ // expects a specific stride value. In that case, we try to guess it by
+ // aligning the width to small powers of 2.
+ // TODO(b/174120849): Avoid stride assumptions.
+ AHardwareBuffer* hardwareBuffer = nullptr;
+ status_t status = UNKNOWN_ERROR;
+ for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
+ const uint32_t stride = roundUpToMultiple(width, alignment);
+ AHardwareBuffer_Desc desc{
+ .width = width,
+ .height = height,
+ .layers = layers,
+ .format = format,
+ .usage = usage,
+ .stride = stride,
+ };
+ status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
+ AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+ &hardwareBuffer);
+ if (status == NO_ERROR) {
+ break;
+ }
+ }
+ if (status != NO_ERROR) {
+ return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+ << "Can't create AHardwareBuffer from handle. Error: " << status;
+ }
+
+ return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
+#else // __ANDROID__
+ LOG(FATAL) << "nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const "
+ "hidl_memory& memory): Not Available on Host Build";
+ return (NN_ERROR() << "createSharedMemoryFromHidlMemory failed")
+ .
+ operator nn::GeneralResult<nn::SharedMemory>();
+#endif // __ANDROID__
+}
+
} // anonymous namespace
GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType) {
@@ -146,8 +267,20 @@
return Model::OperandValues(operandValues.data(), operandValues.size());
}
+GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& handle) {
+ if (handle.getNativeHandle() == nullptr) {
+ return nullptr;
+ }
+ if (handle->numFds != 1 || handle->numInts != 0) {
+ return NN_ERROR()
+ << "unvalidatedConvert failed because handle does not only hold a single fd";
+ }
+ auto duplicatedFd = NN_TRY(nn::dupFd(handle->data[0]));
+ return std::make_shared<const Handle>(std::move(duplicatedFd));
+}
+
GeneralResult<SharedMemory> unvalidatedConvert(const hidl_memory& memory) {
- return hal::utils::createSharedMemoryFromHidlMemory(memory);
+ return createSharedMemoryFromHidlMemory(memory);
}
GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
@@ -155,7 +288,7 @@
// Verify number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+ NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
CHECK(model.operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < model.operands.size(); ++i) {
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -260,6 +393,82 @@
return utils::unvalidatedConvert(canonical);
}
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(std::vector<base::unique_fd> fds,
+ const std::vector<int32_t>& ints) {
+ constexpr size_t kIntMax = std::numeric_limits<int>::max();
+ CHECK_LE(fds.size(), kIntMax);
+ CHECK_LE(ints.size(), kIntMax);
+ native_handle_t* nativeHandle =
+ native_handle_create(static_cast<int>(fds.size()), static_cast<int>(ints.size()));
+ if (nativeHandle == nullptr) {
+ return NN_ERROR() << "Failed to create native_handle";
+ }
+
+ for (size_t i = 0; i < fds.size(); ++i) {
+ nativeHandle->data[i] = fds[i].release();
+ }
+ std::copy(ints.begin(), ints.end(), nativeHandle->data + nativeHandle->numFds);
+
+ hidl_handle handle;
+ handle.setTo(nativeHandle, /*shouldOwn=*/true);
+ return handle;
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
+ const std::vector<int32_t>& ints) {
+ std::vector<base::unique_fd> fds;
+ fds.push_back(std::move(fd));
+ return createNativeHandleFrom(std::move(fds), ints);
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(const nn::Memory::Unknown::Handle& handle) {
+ std::vector<base::unique_fd> fds = NN_TRY(nn::dupFds(handle.fds.begin(), handle.fds.end()));
+ return createNativeHandleFrom(std::move(fds), handle.ints);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+ auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
+ return hidl_memory("ashmem", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
+ auto fd = NN_TRY(nn::dupFd(memory.fd));
+
+ const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
+ const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
+
+ auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
+ return hidl_memory("mmap_fd", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
+#ifdef __ANDROID__
+ const auto* ahwb = memory.handle.get();
+ AHardwareBuffer_Desc bufferDesc;
+ AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+ const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+ const size_t size = isBlob ? bufferDesc.width : 0;
+ const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
+
+ const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+ const hidl_handle hidlHandle(nativeHandle);
+ hidl_handle copiedHandle(hidlHandle);
+
+ return hidl_memory(name, std::move(copiedHandle), size);
+#else // __ANDROID__
+ LOG(FATAL) << "nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const "
+ "nn::Memory::HardwareBuffer& memory): Not Available on Host Build";
+ (void)memory;
+ return (NN_ERROR() << "createHidlMemoryFrom failed").operator nn::GeneralResult<hidl_memory>();
+#endif // __ANDROID__
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
+ return hidl_memory(memory.name, NN_TRY(createNativeHandleFrom(memory.handle)), memory.size);
+}
+
} // anonymous namespace
nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
@@ -332,8 +541,19 @@
return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
}
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+ if (handle == nullptr) {
+ return {};
+ }
+ base::unique_fd fd = NN_TRY(nn::dupFd(handle->get()));
+ return createNativeHandleFrom(std::move(fd), {});
+}
+
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
- return hal::utils::createHidlMemoryFromSharedMemory(memory);
+ if (memory == nullptr) {
+ return NN_ERROR() << "Memory must be non-empty";
+ }
+ return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
}
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
@@ -346,7 +566,7 @@
// Update number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+ NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
CHECK(operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < operands.size(); ++i) {
operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
index 93bd81a..49913a2 100644
--- a/neuralnetworks/1.0/utils/src/Device.cpp
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -18,6 +18,8 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/IDevice.h>
@@ -29,8 +31,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <functional>
@@ -47,7 +47,7 @@
nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
const Capabilities& capabilities) {
- HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
return nn::convert(capabilities);
}
@@ -156,7 +156,7 @@
const auto ret = kDevice->prepareModel(hidlModel, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.0/utils/src/Execution.cpp b/neuralnetworks/1.0/utils/src/Execution.cpp
index 7a3216b..6e105a6 100644
--- a/neuralnetworks/1.0/utils/src/Execution.cpp
+++ b/neuralnetworks/1.0/utils/src/Execution.cpp
@@ -18,6 +18,8 @@
#include "Callbacks.h"
#include "Conversions.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -27,8 +29,6 @@
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <memory>
#include <utility>
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
index 3060c65..00e7d22 100644
--- a/neuralnetworks/1.0/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -20,6 +20,8 @@
#include "Callbacks.h"
#include "Conversions.h"
#include "Execution.h"
+#include "HandleError.h"
+#include "ProtectCallback.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
@@ -28,8 +30,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <memory>
#include <tuple>
@@ -84,7 +84,7 @@
const auto ret = kPreparedModel->execute(request, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
auto result = NN_TRY(cb->get());
if (relocation.output) {
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/1.0/utils/src/ProtectCallback.cpp
similarity index 98%
rename from neuralnetworks/utils/common/src/ProtectCallback.cpp
rename to neuralnetworks/1.0/utils/src/ProtectCallback.cpp
index 18e1f3b..89539b5 100644
--- a/neuralnetworks/utils/common/src/ProtectCallback.cpp
+++ b/neuralnetworks/1.0/utils/src/ProtectCallback.cpp
@@ -22,7 +22,8 @@
#include <android/hidl/base/1.0/IBase.h>
#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
-#include <nnapi/hal/HandleError.h>
+
+#include "HandleError.h"
#include <algorithm>
#include <functional>
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index b33c581..29b31d2 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -50,7 +50,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common",
],
header_libs: [
"libneuralnetworks_headers",
@@ -81,7 +81,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
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..d6bd36a 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
@@ -23,8 +23,8 @@
#include <nnapi/OperandTypes.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <memory>
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
index 09d9fe8..4660ff7 100644
--- a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -26,7 +26,6 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_1::utils {
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
index 467ceb3..5bdbe31 100644
--- a/neuralnetworks/1.1/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -100,7 +100,7 @@
// Verify number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+ NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
CHECK(model.operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < model.operands.size(); ++i) {
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -223,7 +223,7 @@
// Update number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+ NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
CHECK(operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < operands.size(); ++i) {
operands[i].numberOfConsumers = numberOfConsumers[i];
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
index 3197ef4..7d54cab 100644
--- a/neuralnetworks/1.1/utils/src/Device.cpp
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -29,9 +29,9 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <memory>
@@ -47,7 +47,7 @@
nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
const Capabilities& capabilities) {
- HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
return nn::convert(capabilities);
}
@@ -157,7 +157,7 @@
const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index c001112..e9d4b76 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -48,7 +48,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h
similarity index 82%
rename from neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
rename to neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h
index dae1ff3..ac9411c 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Burst.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_H
-#include "ExecutionBurstUtils.h"
+#include "nnapi/hal/1.2/BurstUtils.h"
#include <android-base/thread_annotations.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
@@ -32,8 +32,8 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <atomic>
#include <chrono>
@@ -49,13 +49,11 @@
namespace android::hardware::neuralnetworks::V1_2::utils {
/**
- * 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.
+ * The Burst 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 final
- : public nn::IBurst,
- public std::enable_shared_from_this<ExecutionBurstController> {
+class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst> {
struct PrivateConstructorTag {};
public:
@@ -150,21 +148,21 @@
* Creates a burst controller on a prepared model.
*
* @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.
+ * @param pollingTimeWindow How much time (in microseconds) the Burst 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 Burst Execution burst controller object.
*/
- static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
+ static nn::GeneralResult<std::shared_ptr<const Burst>> create(
nn::SharedPreparedModel preparedModel, const sp<IPreparedModel>& hidlPreparedModel,
std::chrono::microseconds pollingTimeWindow);
- ExecutionBurstController(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel,
- std::unique_ptr<RequestChannelSender> requestChannelSender,
- std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
- sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
- std::shared_ptr<MemoryCache> memoryCache,
- neuralnetworks::utils::DeathHandler deathHandler);
+ Burst(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel,
+ std::unique_ptr<RequestChannelSender> requestChannelSender,
+ std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+ sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+ std::shared_ptr<MemoryCache> memoryCache,
+ neuralnetworks::utils::DeathHandler deathHandler);
// See IBurst::cacheMemory for information on this method.
OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
@@ -202,4 +200,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_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/BurstUtils.h
similarity index 97%
rename from neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
rename to neuralnetworks/1.2/utils/include/nnapi/hal/1.2/BurstUtils.h
index c662bc3..7a6a241 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/BurstUtils.h
@@ -14,8 +14,8 @@
* 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
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_UTILS_H
#include <android/hardware/neuralnetworks/1.0/types.h>
#include <android/hardware/neuralnetworks/1.2/types.h>
@@ -23,7 +23,7 @@
#include <hidl/MQDescriptor.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <atomic>
#include <chrono>
@@ -298,4 +298,4 @@
} // namespace android::hardware::neuralnetworks::V1_2::utils
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_BURST_UTILS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
index ba3c1ba..6dd8138 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -27,8 +27,8 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
index 272cee7..c3348aa 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -45,7 +45,6 @@
GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension);
GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation);
-GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType);
GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities);
@@ -86,7 +85,6 @@
nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension);
nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
const nn::Extension::OperandTypeInformation& operandTypeInformation);
-nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType);
nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
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..e7ac172 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
@@ -23,8 +23,8 @@
#include <nnapi/OperandTypes.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <memory>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
index 9c66446..867f181 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
@@ -21,8 +21,8 @@
#include <nnapi/IExecution.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include "PreparedModel.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 35abd79..1150e5e 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
@@ -22,8 +22,8 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <memory>
#include <tuple>
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
index 5c3b8a7..23e336a 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -28,7 +28,6 @@
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/1.1/Utils.h>
-#include <nnapi/hal/HandleError.h>
#include <limits>
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/Burst.cpp
similarity index 78%
rename from neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
rename to neuralnetworks/1.2/utils/src/Burst.cpp
index 2746965..e0a23f1 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
+++ b/neuralnetworks/1.2/utils/src/Burst.cpp
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-#define LOG_TAG "ExecutionBurstController"
-
-#include "ExecutionBurstController.h"
-#include "ExecutionBurstUtils.h"
+#include "Burst.h"
+#include "BurstUtils.h"
#include <android-base/logging.h>
#include <android-base/thread_annotations.h>
@@ -28,9 +26,9 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <algorithm>
@@ -57,14 +55,13 @@
public:
static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
- std::shared_ptr<const ExecutionBurstController> controller,
- std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
- std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+ std::shared_ptr<const Burst> controller, std::vector<FmqRequestDatum> request,
+ hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds);
- BurstExecution(PrivateConstructorTag tag,
- std::shared_ptr<const ExecutionBurstController> controller,
+ BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> controller,
std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
- std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+ std::vector<Burst::OptionalCacheHold> cacheHolds);
nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
const nn::OptionalTimePoint& deadline) const override;
@@ -74,16 +71,16 @@
const nn::OptionalDuration& timeoutDurationAfterFence) const override;
private:
- const std::shared_ptr<const ExecutionBurstController> kController;
+ const std::shared_ptr<const Burst> kController;
const std::vector<FmqRequestDatum> kRequest;
const hal::utils::RequestRelocation kRelocation;
- const std::vector<ExecutionBurstController::OptionalCacheHold> kCacheHolds;
+ const std::vector<Burst::OptionalCacheHold> kCacheHolds;
};
nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
- HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
- << toString(status);
+ HANDLE_STATUS_HIDL(status) << "IPreparedModel::configureExecutionBurst failed with status "
+ << toString(status);
if (burstContext == nullptr) {
return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
<< "IPreparedModel::configureExecutionBurst returned nullptr for burst";
@@ -92,8 +89,7 @@
}
nn::GeneralResult<hidl_vec<hidl_memory>> getMemoriesHelper(
- const hidl_vec<int32_t>& slots,
- const std::shared_ptr<ExecutionBurstController::MemoryCache>& memoryCache) {
+ const hidl_vec<int32_t>& slots, const std::shared_ptr<Burst::MemoryCache>& memoryCache) {
hidl_vec<hidl_memory> memories(slots.size());
for (size_t i = 0; i < slots.size(); ++i) {
const int32_t slot = slots[i];
@@ -110,7 +106,7 @@
// MemoryCache methods
-ExecutionBurstController::MemoryCache::MemoryCache() {
+Burst::MemoryCache::MemoryCache() {
constexpr size_t kPreallocatedCount = 1024;
std::vector<int32_t> freeSlotsSpace;
freeSlotsSpace.reserve(kPreallocatedCount);
@@ -119,13 +115,13 @@
mCacheCleaner.reserve(kPreallocatedCount);
}
-void ExecutionBurstController::MemoryCache::setBurstContext(sp<IBurstContext> burstContext) {
+void Burst::MemoryCache::setBurstContext(sp<IBurstContext> burstContext) {
std::lock_guard guard(mMutex);
mBurstContext = std::move(burstContext);
}
-std::pair<int32_t, ExecutionBurstController::MemoryCache::SharedCleanup>
-ExecutionBurstController::MemoryCache::cacheMemory(const nn::SharedMemory& memory) {
+std::pair<int32_t, Burst::MemoryCache::SharedCleanup> Burst::MemoryCache::cacheMemory(
+ const nn::SharedMemory& memory) {
std::unique_lock lock(mMutex);
base::ScopedLockAssertion lockAssert(mMutex);
@@ -163,7 +159,7 @@
return std::make_pair(slot, std::move(cleaner));
}
-nn::GeneralResult<nn::SharedMemory> ExecutionBurstController::MemoryCache::getMemory(int32_t slot) {
+nn::GeneralResult<nn::SharedMemory> Burst::MemoryCache::getMemory(int32_t slot) {
std::lock_guard guard(mMutex);
if (slot < 0 || static_cast<size_t>(slot) >= mMemoryCache.size()) {
return NN_ERROR() << "Invalid slot: " << slot << " vs " << mMemoryCache.size();
@@ -171,7 +167,7 @@
return mMemoryCache[slot];
}
-void ExecutionBurstController::MemoryCache::freeMemory(const nn::SharedMemory& memory) {
+void Burst::MemoryCache::freeMemory(const nn::SharedMemory& memory) {
{
std::lock_guard guard(mMutex);
const int32_t slot = mMemoryIdToSlot.at(memory);
@@ -189,7 +185,7 @@
mCond.notify_all();
}
-int32_t ExecutionBurstController::MemoryCache::allocateSlotLocked() {
+int32_t Burst::MemoryCache::allocateSlotLocked() {
constexpr size_t kMaxNumberOfSlots = std::numeric_limits<int32_t>::max();
// If there is a free slot, use it.
@@ -210,18 +206,18 @@
// ExecutionBurstCallback methods
-ExecutionBurstController::ExecutionBurstCallback::ExecutionBurstCallback(
+Burst::ExecutionBurstCallback::ExecutionBurstCallback(
const std::shared_ptr<MemoryCache>& memoryCache)
: kMemoryCache(memoryCache) {
CHECK(memoryCache != nullptr);
}
-Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
- const hidl_vec<int32_t>& slots, getMemories_cb cb) {
+Return<void> Burst::ExecutionBurstCallback::getMemories(const hidl_vec<int32_t>& slots,
+ getMemories_cb cb) {
const auto memoryCache = kMemoryCache.lock();
if (memoryCache == nullptr) {
- LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories called after "
- "the MemoryCache has been freed";
+ LOG(ERROR) << "Burst::ExecutionBurstCallback::getMemories called after the MemoryCache has "
+ "been freed";
cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
return Void();
}
@@ -229,8 +225,8 @@
const auto maybeMemories = getMemoriesHelper(slots, memoryCache);
if (!maybeMemories.has_value()) {
const auto& [message, code] = maybeMemories.error();
- LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories failed with "
- << code << ": " << message;
+ LOG(ERROR) << "Burst::ExecutionBurstCallback::getMemories failed with " << code << ": "
+ << message;
cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
return Void();
}
@@ -239,14 +235,14 @@
return Void();
}
-// ExecutionBurstController methods
+// Burst methods
-nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> ExecutionBurstController::create(
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
nn::SharedPreparedModel preparedModel, const sp<V1_2::IPreparedModel>& hidlPreparedModel,
std::chrono::microseconds pollingTimeWindow) {
// check inputs
if (preparedModel == nullptr || hidlPreparedModel == nullptr) {
- return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
+ return NN_ERROR() << "Burst::create passed a nullptr";
}
// create FMQ objects
@@ -282,18 +278,18 @@
deathHandler.protectCallbackForLifetimeOfDeathHandler(resultChannelReceiver.get());
// make and return controller
- return std::make_shared<const ExecutionBurstController>(
+ return std::make_shared<const Burst>(
PrivateConstructorTag{}, std::move(preparedModel), std::move(requestChannelSender),
std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
std::move(memoryCache), std::move(deathHandler));
}
-ExecutionBurstController::ExecutionBurstController(
- PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel,
- std::unique_ptr<RequestChannelSender> requestChannelSender,
- std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
- sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
- std::shared_ptr<MemoryCache> memoryCache, neuralnetworks::utils::DeathHandler deathHandler)
+Burst::Burst(PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel,
+ std::unique_ptr<RequestChannelSender> requestChannelSender,
+ std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+ sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+ std::shared_ptr<MemoryCache> memoryCache,
+ neuralnetworks::utils::DeathHandler deathHandler)
: kPreparedModel(std::move(preparedModel)),
mRequestChannelSender(std::move(requestChannelSender)),
mResultChannelReceiver(std::move(resultChannelReceiver)),
@@ -302,21 +298,20 @@
mMemoryCache(std::move(memoryCache)),
kDeathHandler(std::move(deathHandler)) {}
-ExecutionBurstController::OptionalCacheHold ExecutionBurstController::cacheMemory(
- const nn::SharedMemory& memory) const {
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) const {
auto [slot, hold] = mMemoryCache->cacheMemory(memory);
return hold;
}
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure,
- const nn::OptionalTimePoint& deadline,
- const nn::OptionalDuration& loopTimeoutDuration) const {
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+ const nn::Request& request, nn::MeasureTiming measure,
+ const nn::OptionalTimePoint& deadline,
+ const nn::OptionalDuration& loopTimeoutDuration) const {
// 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_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "Burst::execute");
// if the request is valid but of a higher version than what's supported in burst execution,
// fall back to another execution path
@@ -357,10 +352,10 @@
}
// See IBurst::createReusableExecution for information on this method.
-nn::GeneralResult<nn::SharedExecution> ExecutionBurstController::createReusableExecution(
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
const nn::Request& request, nn::MeasureTiming measure,
const nn::OptionalDuration& loopTimeoutDuration) const {
- NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::createReusableExecution");
+ NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "Burst::createReusableExecution");
// if the request is valid but of a higher version than what's supported in burst execution,
// fall back to another execution path
@@ -397,12 +392,10 @@
std::move(relocation), std::move(holds));
}
-nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
-ExecutionBurstController::executeInternal(const std::vector<FmqRequestDatum>& requestPacket,
- const hal::utils::RequestRelocation& relocation,
- FallbackFunction fallback) const {
- NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
- "ExecutionBurstController::executeInternal");
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal(
+ const std::vector<FmqRequestDatum>& requestPacket,
+ const hal::utils::RequestRelocation& relocation, FallbackFunction fallback) const {
+ NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION, "Burst::executeInternal");
// Ensure that at most one execution is in flight at any given time.
const bool alreadyInFlight = mExecutionInFlight.test_and_set();
@@ -435,9 +428,9 @@
}
nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
- std::shared_ptr<const ExecutionBurstController> controller,
- std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
- std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds) {
+ std::shared_ptr<const Burst> controller, std::vector<FmqRequestDatum> request,
+ hal::utils::RequestRelocation relocation,
+ std::vector<Burst::OptionalCacheHold> cacheHolds) {
if (controller == nullptr) {
return NN_ERROR() << "V1_2::utils::BurstExecution::create must have non-null controller";
}
@@ -448,10 +441,10 @@
}
BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/,
- std::shared_ptr<const ExecutionBurstController> controller,
+ std::shared_ptr<const Burst> controller,
std::vector<FmqRequestDatum> request,
hal::utils::RequestRelocation relocation,
- std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds)
+ std::vector<Burst::OptionalCacheHold> cacheHolds)
: kController(std::move(controller)),
kRequest(std::move(request)),
kRelocation(std::move(relocation)),
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/BurstUtils.cpp
similarity index 99%
rename from neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
rename to neuralnetworks/1.2/utils/src/BurstUtils.cpp
index 1bdde1e..b589c46 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
+++ b/neuralnetworks/1.2/utils/src/BurstUtils.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "ExecutionBurstUtils"
-
-#include "ExecutionBurstUtils.h"
+#include "BurstUtils.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -27,7 +25,7 @@
#include <hidl/MQDescriptor.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <atomic>
#include <chrono>
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
index 01b5e12..cb61f21 100644
--- a/neuralnetworks/1.2/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -29,10 +29,10 @@
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Callbacks.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <utility>
@@ -62,7 +62,7 @@
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
}
@@ -74,7 +74,7 @@
return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
<< "execution failed with " << toString(status);
}
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
return convertExecutionGeneralResultsHelper(outputShapes, timing);
}
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
index 6a80b42..838d9c4 100644
--- a/neuralnetworks/1.2/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -28,7 +28,6 @@
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <algorithm>
#include <functional>
@@ -187,7 +186,7 @@
// Verify number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(model.operands.size(), operations));
+ NN_TRY(countNumberOfConsumers(model.operands.size(), operations));
CHECK(model.operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < model.operands.size(); ++i) {
if (model.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -264,14 +263,6 @@
};
}
-GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
- if (hidlHandle.getNativeHandle() == nullptr) {
- return nullptr;
- }
- auto handle = NN_TRY(hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle()));
- return std::make_shared<const Handle>(std::move(handle));
-}
-
GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
return validatedConvert(deviceType);
}
@@ -334,6 +325,10 @@
return V1_0::utils::unvalidatedConvert(operandValues);
}
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+ return V1_0::utils::unvalidatedConvert(handle);
+}
+
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
return V1_0::utils::unvalidatedConvert(memory);
}
@@ -481,7 +476,7 @@
// Update number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), model.main.operations));
+ NN_TRY(countNumberOfConsumers(operands.size(), model.main.operations));
CHECK(operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < operands.size(); ++i) {
operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -544,13 +539,6 @@
};
}
-nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
- if (handle == nullptr) {
- return {};
- }
- return hal::utils::hidlHandleFromSharedHandle(*handle);
-}
-
nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
return validatedConvert(deviceType);
}
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
index 9fe0de2..f12669a 100644
--- a/neuralnetworks/1.2/utils/src/Device.cpp
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -30,10 +30,10 @@
#include <nnapi/OperandTypes.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <memory>
@@ -49,31 +49,31 @@
nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
const Capabilities& capabilities) {
- HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
return nn::convert(capabilities);
}
nn::GeneralResult<std::string> versionStringCallback(V1_0::ErrorStatus status,
const hidl_string& versionString) {
- HANDLE_HAL_STATUS(status) << "getVersionString failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getVersionString failed with " << toString(status);
return versionString;
}
nn::GeneralResult<nn::DeviceType> deviceTypeCallback(V1_0::ErrorStatus status,
DeviceType deviceType) {
- HANDLE_HAL_STATUS(status) << "getDeviceType failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getDeviceType failed with " << toString(status);
return nn::convert(deviceType);
}
nn::GeneralResult<std::vector<nn::Extension>> supportedExtensionsCallback(
V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
- HANDLE_HAL_STATUS(status) << "getExtensions failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getExtensions failed with " << toString(status);
return nn::convert(extensions);
}
nn::GeneralResult<std::pair<uint32_t, uint32_t>> numberOfCacheFilesNeededCallback(
V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
- HANDLE_HAL_STATUS(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
if (numModelCache > nn::kMaxNumberOfCacheFiles) {
return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
"than allowed max ("
@@ -254,7 +254,7 @@
const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return cb->get();
}
@@ -271,7 +271,7 @@
const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation from cache failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.2/utils/src/Execution.cpp b/neuralnetworks/1.2/utils/src/Execution.cpp
index 18d1c90..320b0e1 100644
--- a/neuralnetworks/1.2/utils/src/Execution.cpp
+++ b/neuralnetworks/1.2/utils/src/Execution.cpp
@@ -29,7 +29,6 @@
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <memory>
#include <utility>
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
index c261184..6df3df3 100644
--- a/neuralnetworks/1.2/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -16,11 +16,11 @@
#include "PreparedModel.h"
+#include "Burst.h"
+#include "BurstUtils.h"
#include "Callbacks.h"
#include "Conversions.h"
#include "Execution.h"
-#include "ExecutionBurstController.h"
-#include "ExecutionBurstUtils.h"
#include "Utils.h"
#include <android/hardware/neuralnetworks/1.0/types.h>
@@ -31,9 +31,9 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <chrono>
#include <memory>
@@ -82,7 +82,7 @@
const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
if (status != V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
}
return cb->get();
@@ -150,16 +150,8 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](
- const nn::Request& request, nn::MeasureTiming measure,
- const nn::OptionalTimePoint& deadline,
- const nn::OptionalDuration& loopTimeoutDuration)
- -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
- };
const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
- return ExecutionBurstController::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
+ return Burst::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index e313b47..52d51e2 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -71,7 +71,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common",
],
whole_static_libs: [
"neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
index 643172e..4b8ddc1 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -30,8 +30,8 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
index b677c62..ec1e530 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -113,6 +113,9 @@
nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing);
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+ const std::vector<nn::SyncFence>& fences);
+
} // namespace android::hardware::neuralnetworks::V1_3::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_CONVERSIONS_H
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..c3c6fc4 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
@@ -23,8 +23,8 @@
#include <nnapi/OperandTypes.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <memory>
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 5acba71..480438d 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
@@ -21,8 +21,8 @@
#include <nnapi/IPreparedModel.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <memory>
#include <tuple>
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
index 28525bd..2812db2 100644
--- a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -30,7 +30,6 @@
#include <nnapi/hal/1.1/Utils.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.2/Utils.h>
-#include <nnapi/hal/HandleError.h>
namespace android::hardware::neuralnetworks::V1_3::utils {
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
index ada5265..34925ea 100644
--- a/neuralnetworks/1.3/utils/src/Buffer.cpp
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -25,7 +25,7 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/1.0/HandleError.h>
#include "Conversions.h"
#include "Utils.h"
@@ -66,7 +66,7 @@
const auto ret = kBuffer->copyTo(hidlDst);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "IBuffer::copyTo failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "IBuffer::copyTo failed with " << toString(status);
return {};
}
@@ -78,7 +78,7 @@
const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "IBuffer::copyFrom failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "IBuffer::copyFrom failed with " << toString(status);
return {};
}
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
index 156216f..f063862 100644
--- a/neuralnetworks/1.3/utils/src/Callbacks.cpp
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -30,13 +30,13 @@
#include <nnapi/Types.h>
#include <nnapi/hal/1.0/Callbacks.h>
#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/HandleError.h>
#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/1.2/Callbacks.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.2/PreparedModel.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <nnapi/hal/TransferValue.h>
#include <utility>
@@ -71,13 +71,13 @@
nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
- HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "get supported operations failed with " << toString(status);
return supportedOperations;
}
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
}
@@ -90,7 +90,7 @@
return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
<< "execution failed with " << toString(status);
}
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
return convertExecutionGeneralResultsHelper(outputShapes, timing);
}
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
index b35b2cd..a1d414c 100644
--- a/neuralnetworks/1.3/utils/src/Conversions.cpp
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -28,7 +28,6 @@
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <algorithm>
#include <chrono>
@@ -194,7 +193,7 @@
// Verify number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(subgraph.operands.size(), operations));
+ NN_TRY(countNumberOfConsumers(subgraph.operands.size(), operations));
CHECK(subgraph.operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < subgraph.operands.size(); ++i) {
if (subgraph.operands[i].numberOfConsumers != numberOfConsumers[i]) {
@@ -380,7 +379,7 @@
}
nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
- return V1_2::utils::unvalidatedConvert(handle);
+ return V1_0::utils::unvalidatedConvert(handle);
}
nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
@@ -543,7 +542,7 @@
// Update number of consumers.
const auto numberOfConsumers =
- NN_TRY(hal::utils::countNumberOfConsumers(operands.size(), subgraph.operations));
+ NN_TRY(countNumberOfConsumers(operands.size(), subgraph.operations));
CHECK(operands.size() == numberOfConsumers.size());
for (size_t i = 0; i < operands.size(); ++i) {
operands[i].numberOfConsumers = numberOfConsumers[i];
@@ -727,4 +726,13 @@
return V1_2::utils::convert(timing);
}
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+ const std::vector<nn::SyncFence>& syncFences) {
+ std::vector<nn::SharedHandle> handles;
+ handles.reserve(syncFences.size());
+ std::transform(syncFences.begin(), syncFences.end(), std::back_inserter(handles),
+ [](const nn::SyncFence& syncFence) { return syncFence.getSharedHandle(); });
+ return convert(handles);
+}
+
} // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
index d710b85..a73ce82 100644
--- a/neuralnetworks/1.3/utils/src/Device.cpp
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -33,13 +33,13 @@
#include <nnapi/OperandTypes.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.2/Device.h>
#include <nnapi/hal/1.2/Utils.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <any>
#include <functional>
@@ -72,7 +72,7 @@
nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
const Capabilities& capabilities) {
- HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getting capabilities failed with " << toString(status);
return nn::convert(capabilities);
}
@@ -89,7 +89,7 @@
nn::GeneralResult<nn::SharedBuffer> allocationCallback(ErrorStatus status,
const sp<IBuffer>& buffer, uint32_t token) {
- HANDLE_HAL_STATUS(status) << "IDevice::allocate failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "IDevice::allocate failed with " << toString(status);
return Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token));
}
@@ -208,7 +208,7 @@
kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
hidlModelCache, hidlDataCache, hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation failed with " << toString(status);
return cb->get();
}
@@ -227,7 +227,7 @@
const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
hidlToken, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
- HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "model preparation from cache failed with " << toString(status);
return cb->get();
}
diff --git a/neuralnetworks/1.3/utils/src/Execution.cpp b/neuralnetworks/1.3/utils/src/Execution.cpp
index 4dc0ddf..0ec7f56 100644
--- a/neuralnetworks/1.3/utils/src/Execution.cpp
+++ b/neuralnetworks/1.3/utils/src/Execution.cpp
@@ -29,7 +29,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <memory>
#include <utility>
@@ -73,7 +72,7 @@
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
const nn::OptionalDuration& timeoutDurationAfterFence) const {
- const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+ const auto hidlWaitFor = NN_TRY(convertSyncFences(waitFor));
const auto hidlDeadline = NN_TRY(convert(deadline));
const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
return kPreparedModel->executeFencedInternal(kRequest, hidlWaitFor, kMeasure, hidlDeadline,
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
index d5dee9d..ce977e5 100644
--- a/neuralnetworks/1.3/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -30,12 +30,12 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
+#include <nnapi/hal/1.2/Burst.h>
+#include <nnapi/hal/1.2/BurstUtils.h>
#include <nnapi/hal/1.2/Conversions.h>
-#include <nnapi/hal/1.2/ExecutionBurstController.h>
-#include <nnapi/hal/1.2/ExecutionBurstUtils.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <memory>
#include <tuple>
@@ -50,14 +50,14 @@
nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
ErrorStatus status, const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
- HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "fenced execution callback info failed with " << toString(status);
return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
}
nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> fencedExecutionCallback(
ErrorStatus status, const hidl_handle& syncFence,
const sp<IFencedExecutionCallback>& callback) {
- HANDLE_HAL_STATUS(status) << "fenced execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "fenced execution failed with " << toString(status);
auto resultSyncFence = nn::SyncFence::createAsSignaled();
if (syncFence.getNativeHandle() != nullptr) {
@@ -127,7 +127,7 @@
kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
const auto status = HANDLE_TRANSPORT_FAILURE(ret);
if (status != ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
- HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "execution failed with " << toString(status);
}
return cb->get();
@@ -186,7 +186,7 @@
&maybeRequestInShared, &relocation));
const auto hidlRequest = NN_TRY(convert(requestInShared));
- const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+ const auto hidlWaitFor = NN_TRY(convertSyncFences(waitFor));
const auto hidlMeasure = NN_TRY(convert(measure));
const auto hidlDeadline = NN_TRY(convert(deadline));
const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
@@ -246,17 +246,8 @@
}
nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
- auto self = shared_from_this();
- auto fallback = [preparedModel = std::move(self)](
- const nn::Request& request, nn::MeasureTiming measure,
- const nn::OptionalTimePoint& deadline,
- const nn::OptionalDuration& loopTimeoutDuration)
- -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
- return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
- };
const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
- return V1_2::utils::ExecutionBurstController::create(shared_from_this(), kPreparedModel,
- pollingTimeWindow);
+ return V1_2::utils::Burst::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
}
std::any PreparedModel::getUnderlyingResource() const {
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index ab0a018..8951760 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -66,7 +66,6 @@
"VtsHalNeuralNetworksV1_0_utils",
"VtsHalNeuralNetworksV1_2_utils",
"VtsHalNeuralNetworksV1_3_utils",
- "android.hardware.neuralnetworks-V2-ndk",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
@@ -76,7 +75,7 @@
"libgmock",
"libhidlmemory",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
+ "libneuralnetworks_common",
"libsync",
],
whole_static_libs: [
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
index 4259143..2eff11b 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -137,4 +137,5 @@
FILL = 100,
RANK = 101,
BATCH_MATMUL = 102,
+ PACK = 103,
}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index d9951d5..2ec91ac 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -5273,4 +5273,53 @@
* c_o = r_y if adj_y else c_y
*/
BATCH_MATMUL = 102,
+
+ /**
+ * Packs N input tensors (N >= 1) of rank R into one output tensor of rank R+1.
+ * The tensors are packed along a given axis.
+ *
+ * The input tensors must have identical {@link OperandType} and dimensions.
+ *
+ * For example, suppose there are N input tensors of shape (A, B, C).
+ * If axis is 0, the output tensor will have shape (N, A, B, C).
+ * If axis is 1, the output tensor will have shape (A, N, B, C).
+ *
+ * All dimensions through the axis dimension determine the output tile count;
+ * the remaining dimensions determine the tile shape.
+ *
+ * Return to the example of N input tensors of shape (A, B, C).
+ * If axis is 0, there are N tiles in the output, each of shape (A, B, C).
+ * If axis is 1, there are A*N tiles in the output, each of shape (B, C).
+ *
+ * The coordinates of a tile within the output tensor are (t[0],...,t[axis]).
+ * The coordinates of a tile within an input tensor are (t[0],...,t[axis-1]).
+ * (If axis is 0, an input tensor consists of a single tile.)
+ * If we index input tensors starting with 0 (rather than by operand number),
+ * then output_tile[t[0],...,t[axis]] = input_tile[t[axis]][t[0],...,t[axis-1]].
+ * That is, all output tile coordinates except for the axis coordinate select
+ * the corresponding location within some input tensor; and the axis coordinate
+ * selects the input tensor.
+ *
+ * Supported tensor {@link OperandType}:
+ * * {@link OperandType::TENSOR_FLOAT16}
+ * * {@link OperandType::TENSOR_FLOAT32}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+ * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+ * * {@link OperandType::TENSOR_INT32}
+ *
+ * Supported input tensor rank: from 1
+ *
+ * Inputs:
+ * * 0: A scalar of type {@link OperandType::INT32}, specifying
+ * the axis along which to pack. The valid range is [0, R+1).
+ * * 1 ~ N: Input tensors to be packed together.
+ * For {@link OperandType::TENSOR_QUANT8_ASYMM} and
+ * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensors,
+ * the scales and zeroPoint must be the same for all input tensors,
+ * and will be the same for the output tensor.
+ *
+ * Outputs:
+ * * 0: The packed tensor.
+ */
+ PACK = 103,
}
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index cb67b84..bf8fc2d 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -23,8 +23,8 @@
default_applicable_licenses: ["hardware_interfaces_license"],
}
-cc_library_static {
- name: "neuralnetworks_utils_hal_aidl",
+cc_defaults {
+ name: "neuralnetworks_utils_hal_aidl_defaults",
defaults: ["neuralnetworks_utils_defaults"],
srcs: ["src/*"],
local_include_dirs: ["include/nnapi/hal/aidl/"],
@@ -38,9 +38,7 @@
"neuralnetworks_utils_hal_common",
],
shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
"libbinder_ndk",
- "libhidlbase",
],
target: {
android: {
@@ -49,21 +47,49 @@
},
}
-cc_test {
- name: "neuralnetworks_utils_hal_aidl_test",
- defaults: ["neuralnetworks_utils_defaults"],
- srcs: [
- "test/*.cpp",
+cc_library_static {
+ name: "neuralnetworks_utils_hal_aidl_v1",
+ defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
+ shared_libs: [
+ "android.hardware.neuralnetworks-V1-ndk",
],
+}
+
+cc_library_static {
+ name: "neuralnetworks_utils_hal_aidl",
+ defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
+ shared_libs: [
+ "android.hardware.neuralnetworks-V2-ndk",
+ ],
+}
+
+// A cc_defaults that includes the latest non-experimental AIDL utilities and other AIDL libraries
+// that are commonly used together. Modules that always depend on the latest non-experimental
+// AIDL features can include this cc_defaults to avoid managing dependency versions explicitly.
+cc_defaults {
+ name: "neuralnetworks_use_latest_utils_hal_aidl",
static_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.graphics.common-V2-ndk",
"android.hardware.neuralnetworks-V2-ndk",
+ "neuralnetworks_utils_hal_aidl",
+ ],
+}
+
+cc_test {
+ name: "neuralnetworks_utils_hal_aidl_test",
+ defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
+ "neuralnetworks_utils_defaults",
+ ],
+ srcs: [
+ "test/*.cpp",
+ ],
+ static_libs: [
"libaidlcommonsupport",
"libgmock",
"libneuralnetworks_common",
"neuralnetworks_types",
- "neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
diff --git a/neuralnetworks/aidl/utils/include/AidlBufferTracker.h b/neuralnetworks/aidl/utils/include/AidlBufferTracker.h
new file mode 100644
index 0000000..4421876
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/AidlBufferTracker.h
@@ -0,0 +1,17 @@
+/*
+ * 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 "nnapi/hal/aidl/BufferTracker.h"
diff --git a/neuralnetworks/aidl/utils/include/AidlHalInterfaces.h b/neuralnetworks/aidl/utils/include/AidlHalInterfaces.h
new file mode 100644
index 0000000..3777969
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/AidlHalInterfaces.h
@@ -0,0 +1,17 @@
+/*
+ * 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 "nnapi/hal/aidl/HalInterfaces.h"
diff --git a/neuralnetworks/aidl/utils/include/AidlHalUtils.h b/neuralnetworks/aidl/utils/include/AidlHalUtils.h
new file mode 100644
index 0000000..98bfbad
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/AidlHalUtils.h
@@ -0,0 +1,17 @@
+/*
+ * 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 "nnapi/hal/aidl/HalUtils.h"
diff --git a/neuralnetworks/aidl/utils/include/AidlValidateHal.h b/neuralnetworks/aidl/utils/include/AidlValidateHal.h
new file mode 100644
index 0000000..5aaa74d
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/AidlValidateHal.h
@@ -0,0 +1,17 @@
+/*
+ * 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 "nnapi/hal/aidl/ValidateHal.h"
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/BufferTracker.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/BufferTracker.h
new file mode 100644
index 0000000..18d01e2
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/BufferTracker.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_TRACKER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_TRACKER_H
+
+#include <android-base/macros.h>
+#include <android-base/thread_annotations.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <stack>
+#include <utility>
+#include <vector>
+
+#include "nnapi/hal/aidl/HalInterfaces.h"
+#include "nnapi/hal/aidl/ValidateHal.h"
+
+namespace android::nn {
+
+// This class manages a CPU buffer allocated on heap and provides validation methods.
+class AidlManagedBuffer {
+ public:
+ static std::shared_ptr<AidlManagedBuffer> create(uint32_t size,
+ std::set<AidlHalPreparedModelRole> roles,
+ const Operand& operand);
+
+ // Prefer AidlManagedBuffer::create.
+ AidlManagedBuffer(std::unique_ptr<uint8_t[]> buffer, uint32_t size,
+ std::set<AidlHalPreparedModelRole> roles, const Operand& operand);
+
+ uint8_t* getPointer() const { return kBuffer.get(); }
+ uint32_t getSize() const { return kSize; }
+
+ // "poolIndex" is the index of this buffer in the request.pools.
+ ErrorStatus validateRequest(uint32_t poolIndex, const Request& request,
+ const aidl_hal::IPreparedModel* preparedModel) const;
+
+ // "size" is the byte size of the Memory provided to the copyFrom or copyTo method.
+ ErrorStatus validateCopyFrom(const std::vector<uint32_t>& dimensions, uint32_t size) const;
+ ErrorStatus validateCopyTo(uint32_t size) const;
+
+ bool updateDimensions(const std::vector<uint32_t>& dimensions);
+ void setInitialized(bool initialized);
+
+ private:
+ mutable std::mutex mMutex;
+ const std::unique_ptr<uint8_t[]> kBuffer;
+ const uint32_t kSize;
+ const std::set<AidlHalPreparedModelRole> kRoles;
+ const OperandType kOperandType;
+ const std::vector<uint32_t> kInitialDimensions;
+ std::vector<uint32_t> mUpdatedDimensions GUARDED_BY(mMutex);
+ bool mInitialized GUARDED_BY(mMutex) = false;
+};
+
+// Keep track of all AidlManagedBuffers and assign each with a unique token.
+class AidlBufferTracker : public std::enable_shared_from_this<AidlBufferTracker> {
+ DISALLOW_COPY_AND_ASSIGN(AidlBufferTracker);
+
+ public:
+ // A RAII class to help manage the lifetime of the token.
+ // It is only supposed to be constructed in AidlBufferTracker::add.
+ class Token {
+ DISALLOW_COPY_AND_ASSIGN(Token);
+
+ public:
+ Token(uint32_t token, std::shared_ptr<AidlBufferTracker> tracker)
+ : kToken(token), kBufferTracker(std::move(tracker)) {}
+ ~Token() { kBufferTracker->free(kToken); }
+ uint32_t get() const { return kToken; }
+
+ private:
+ const uint32_t kToken;
+ const std::shared_ptr<AidlBufferTracker> kBufferTracker;
+ };
+
+ // The factory of AidlBufferTracker. This ensures that the AidlBufferTracker is always managed
+ // by a shared_ptr.
+ static std::shared_ptr<AidlBufferTracker> create() {
+ return std::make_shared<AidlBufferTracker>();
+ }
+
+ // Prefer AidlBufferTracker::create.
+ AidlBufferTracker() : mTokenToBuffers(1) {}
+
+ std::unique_ptr<Token> add(std::shared_ptr<AidlManagedBuffer> buffer);
+ std::shared_ptr<AidlManagedBuffer> get(uint32_t token) const;
+
+ private:
+ void free(uint32_t token);
+
+ mutable std::mutex mMutex;
+ std::stack<uint32_t, std::vector<uint32_t>> mFreeTokens GUARDED_BY(mMutex);
+
+ // Since the tokens are allocated in a non-sparse way, we use a vector to represent the mapping.
+ // The index of the vector is the token. When the token gets freed, the corresponding entry is
+ // set to nullptr. mTokenToBuffers[0] is always set to nullptr because 0 is an invalid token.
+ std::vector<std::shared_ptr<AidlManagedBuffer>> mTokenToBuffers GUARDED_BY(mMutex);
+};
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_TRACKER_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
index 8651912..168264b 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
@@ -32,8 +32,7 @@
namespace aidl::android::hardware::neuralnetworks::utils {
// An AIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
-class PreparedModelCallback final : public BnPreparedModelCallback,
- public hal::utils::IProtectedCallback {
+class PreparedModelCallback final : public BnPreparedModelCallback, public IProtectedCallback {
public:
using Data = nn::GeneralResult<nn::SharedPreparedModel>;
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
index 1457646..d558f66 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
@@ -43,11 +43,12 @@
public:
static nn::GeneralResult<std::shared_ptr<const Device>> create(
- std::string name, std::shared_ptr<aidl_hal::IDevice> device);
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel);
Device(PrivateConstructorTag tag, std::string name, std::string versionString,
- nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
- nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+ nn::Version featureLevel, nn::DeviceType deviceType,
+ std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
+ std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler);
const std::string& getName() const override;
@@ -84,6 +85,7 @@
private:
const std::string kName;
const std::string kVersionString;
+ const nn::Version kFeatureLevel;
const nn::DeviceType kDeviceType;
const std::vector<nn::Extension> kExtensions;
const nn::Capabilities kCapabilities;
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h
new file mode 100644
index 0000000..3fb443c
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalInterfaces.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_INTERFACES_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_INTERFACES_H
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/BnBurst.h>
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModelCallback.h>
+#include <aidl/android/hardware/neuralnetworks/BufferDesc.h>
+#include <aidl/android/hardware/neuralnetworks/BufferRole.h>
+#include <aidl/android/hardware/neuralnetworks/Capabilities.h>
+#include <aidl/android/hardware/neuralnetworks/DataLocation.h>
+#include <aidl/android/hardware/neuralnetworks/DeviceBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/DeviceType.h>
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/ExecutionPreference.h>
+#include <aidl/android/hardware/neuralnetworks/Extension.h>
+#include <aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.h>
+#include <aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.h>
+#include <aidl/android/hardware/neuralnetworks/FusedActivationFunc.h>
+#include <aidl/android/hardware/neuralnetworks/IBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModelCallback.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModelParcel.h>
+#include <aidl/android/hardware/neuralnetworks/Memory.h>
+#include <aidl/android/hardware/neuralnetworks/Model.h>
+#include <aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.h>
+#include <aidl/android/hardware/neuralnetworks/Operand.h>
+#include <aidl/android/hardware/neuralnetworks/OperandExtraParams.h>
+#include <aidl/android/hardware/neuralnetworks/OperandLifeTime.h>
+#include <aidl/android/hardware/neuralnetworks/OperandPerformance.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/Operation.h>
+#include <aidl/android/hardware/neuralnetworks/OperationType.h>
+#include <aidl/android/hardware/neuralnetworks/OutputShape.h>
+#include <aidl/android/hardware/neuralnetworks/PerformanceInfo.h>
+#include <aidl/android/hardware/neuralnetworks/Priority.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <aidl/android/hardware/neuralnetworks/RequestArgument.h>
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
+#include <aidl/android/hardware/neuralnetworks/Subgraph.h>
+#include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h>
+#include <aidl/android/hardware/neuralnetworks/Timing.h>
+
+namespace android::nn {
+
+namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
+
+inline constexpr aidl_hal::Priority kDefaultPriorityAidl = aidl_hal::Priority::MEDIUM;
+
+} // namespace android::nn
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_INTERFACES_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalUtils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalUtils.h
new file mode 100644
index 0000000..89552bc
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/HalUtils.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_UTILS_H
+
+#include <vector>
+
+#include "nnapi/hal/aidl/HalInterfaces.h"
+
+namespace android {
+namespace nn {
+
+// Return a vector with one entry for each non-extension OperandType except
+// SUBGRAPH, set to the specified PerformanceInfo value. The vector will be
+// sorted by OperandType.
+//
+// Control flow (OperandType::SUBGRAPH) operation performance is specified
+// separately using Capabilities::ifPerformance and
+// Capabilities::whilePerformance.
+std::vector<aidl_hal::OperandPerformance> nonExtensionOperandPerformance(
+ aidl_hal::PerformanceInfo perf);
+
+// Update the vector entry corresponding to the specified OperandType with the
+// specified PerformanceInfo value. The vector must already have an entry for
+// that OperandType, and must be sorted by OperandType.
+void update(std::vector<aidl_hal::OperandPerformance>* operandPerformance,
+ aidl_hal::OperandType type, aidl_hal::PerformanceInfo perf);
+
+// Returns true if an operand type is an extension type.
+bool isExtensionOperandType(aidl_hal::OperandType type);
+
+// Returns true if an operand type is a scalar type.
+bool isNonExtensionScalar(aidl_hal::OperandType type);
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_HAL_UTILS_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
index ab1108c..92ed1cd 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
@@ -23,7 +23,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <functional>
#include <mutex>
@@ -34,19 +33,39 @@
namespace aidl::android::hardware::neuralnetworks::utils {
+class IProtectedCallback {
+ public:
+ /**
+ * Marks this object as a dead object.
+ */
+ virtual void notifyAsDeadObject() = 0;
+
+ // Public virtual destructor to allow objects to be stored (and destroyed) as smart pointers.
+ // E.g., std::unique_ptr<IProtectedCallback>.
+ virtual ~IProtectedCallback() = default;
+
+ protected:
+ // Protect the non-destructor special member functions to prevent object slicing.
+ IProtectedCallback() = default;
+ IProtectedCallback(const IProtectedCallback&) = default;
+ IProtectedCallback(IProtectedCallback&&) noexcept = default;
+ IProtectedCallback& operator=(const IProtectedCallback&) = default;
+ IProtectedCallback& operator=(IProtectedCallback&&) noexcept = default;
+};
+
// Thread safe class
class DeathMonitor final {
public:
static void serviceDied(void* cookie);
void serviceDied();
// Precondition: `killable` must be non-null.
- void add(hal::utils::IProtectedCallback* killable) const;
+ void add(IProtectedCallback* killable) const;
// Precondition: `killable` must be non-null.
- void remove(hal::utils::IProtectedCallback* killable) const;
+ void remove(IProtectedCallback* killable) const;
private:
mutable std::mutex mMutex;
- mutable std::vector<hal::utils::IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+ mutable std::vector<IProtectedCallback*> mObjects GUARDED_BY(mMutex);
};
class DeathHandler final {
@@ -62,7 +81,7 @@
using Cleanup = std::function<void()>;
// Precondition: `killable` must be non-null.
[[nodiscard]] ::android::base::ScopeGuard<Cleanup> protectCallback(
- hal::utils::IProtectedCallback* killable) const;
+ IProtectedCallback* killable) const;
std::shared_ptr<DeathMonitor> getDeathMonitor() const { return kDeathMonitor; }
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index 1b149e4..f2ab479 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -24,12 +24,11 @@
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
-#include <nnapi/hal/HandleError.h>
namespace aidl::android::hardware::neuralnetworks::utils {
constexpr auto kDefaultPriority = Priority::MEDIUM;
-constexpr auto kVersion = nn::Version::ANDROID_S;
+constexpr auto kVersion = nn::Version::FEATURE_LEVEL_6;
template <typename Type>
nn::Result<void> validate(const Type& halObject) {
@@ -75,6 +74,13 @@
for (const auto status = handleTransportError(ret); !status.ok();) \
return NN_ERROR(status.error().code) << status.error().message << ": "
+#define HANDLE_STATUS_AIDL(status) \
+ if (const ::android::nn::ErrorStatus canonical = ::android::nn::convert(status).value_or( \
+ ::android::nn::ErrorStatus::GENERAL_FAILURE); \
+ canonical == ::android::nn::ErrorStatus::NONE) { \
+ } else \
+ return NN_ERROR(canonical)
+
} // namespace aidl::android::hardware::neuralnetworks::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ValidateHal.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ValidateHal.h
new file mode 100644
index 0000000..62aba31
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ValidateHal.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_VALIDATE_HAL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_VALIDATE_HAL_H
+
+#include "nnapi/hal/aidl/HalInterfaces.h"
+
+#include <memory>
+#include <set>
+#include <tuple>
+#include <vector>
+
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Validation.h>
+
+namespace android {
+namespace nn {
+
+using AidlHalPreparedModelRole = std::tuple<const aidl_hal::IPreparedModel*, IOType, uint32_t>;
+
+bool validateMemoryDesc(
+ const aidl_hal::BufferDesc& desc,
+ const std::vector<std::shared_ptr<aidl_hal::IPreparedModel>>& preparedModels,
+ const std::vector<aidl_hal::BufferRole>& inputRoles,
+ const std::vector<aidl_hal::BufferRole>& outputRoles,
+ std::function<const aidl_hal::Model*(const std::shared_ptr<aidl_hal::IPreparedModel>&)>
+ getModel,
+ std::set<AidlHalPreparedModelRole>* preparedModelRoles, aidl_hal::Operand* combinedOperand);
+
+} // namespace nn
+} // namespace android
+
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_VALIDATE_HAL_H
diff --git a/neuralnetworks/aidl/utils/src/AidlBufferTracker.cpp b/neuralnetworks/aidl/utils/src/AidlBufferTracker.cpp
new file mode 100644
index 0000000..15d0810
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/AidlBufferTracker.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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 "AidlBufferTracker.h"
+
+#include <android-base/macros.h>
+
+#include <memory>
+#include <mutex>
+#include <set>
+#include <stack>
+#include <utility>
+#include <vector>
+
+#include "AidlHalInterfaces.h"
+#include "nnapi/TypeUtils.h"
+
+namespace android::nn {
+
+std::shared_ptr<AidlManagedBuffer> AidlManagedBuffer::create(
+ uint32_t size, std::set<AidlHalPreparedModelRole> roles, const Operand& operand) {
+ std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[size]);
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+ if (isExtension(operand.type)) {
+ LOG(ERROR) << "AidlManagedBuffer cannot handle extension operands.";
+ return nullptr;
+ }
+ return std::make_shared<AidlManagedBuffer>(std::move(buffer), size, std::move(roles), operand);
+}
+
+AidlManagedBuffer::AidlManagedBuffer(std::unique_ptr<uint8_t[]> buffer, uint32_t size,
+ std::set<AidlHalPreparedModelRole> roles,
+ const Operand& operand)
+ : kBuffer(std::move(buffer)),
+ kSize(size),
+ kRoles(std::move(roles)),
+ kOperandType(operand.type),
+ kInitialDimensions(operand.dimensions),
+ mUpdatedDimensions(operand.dimensions) {
+ CHECK(!isExtension(kOperandType));
+}
+
+ErrorStatus AidlManagedBuffer::validateRequest(
+ uint32_t poolIndex, const Request& request,
+ const aidl_hal::IPreparedModel* preparedModel) const {
+ CHECK_LT(poolIndex, request.pools.size());
+ CHECK(std::holds_alternative<Request::MemoryDomainToken>(request.pools[poolIndex]));
+ std::lock_guard<std::mutex> guard(mMutex);
+
+ bool usedAsInput = false, usedAsOutput = false;
+ for (uint32_t i = 0; i < request.inputs.size(); i++) {
+ if (request.inputs[i].lifetime != Request::Argument::LifeTime::POOL) continue;
+ if (request.inputs[i].location.poolIndex != poolIndex) continue;
+ // Validate if the input role is specified during allocation.
+ if (kRoles.count({preparedModel, IOType::INPUT, i}) == 0) {
+ LOG(ERROR) << "AidlManagedBuffer::validateRequest -- invalid buffer role.";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ if (!mInitialized) {
+ LOG(ERROR)
+ << "AidlManagedBuffer::validateRequest -- using uninitialized buffer as input "
+ "request.";
+ return ErrorStatus::GENERAL_FAILURE;
+ }
+ auto combined = combineDimensions(mUpdatedDimensions, request.inputs[i].dimensions);
+ if (!combined.has_value()) {
+ LOG(ERROR) << "AidlManagedBuffer::validateRequest -- incompatible dimensions ("
+ << toString(mUpdatedDimensions) << " vs "
+ << toString(request.inputs[i].dimensions) << ")";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ usedAsInput = true;
+ }
+ for (uint32_t i = 0; i < request.outputs.size(); i++) {
+ if (request.outputs[i].lifetime != Request::Argument::LifeTime::POOL) continue;
+ if (request.outputs[i].location.poolIndex != poolIndex) continue;
+ if (usedAsInput || usedAsOutput) {
+ LOG(ERROR) << "AidlManagedBuffer::validateRequest -- using the same device memory for "
+ "input/output or multiple outputs";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ // Validate if the output role is specified during allocation.
+ if (kRoles.count({preparedModel, IOType::OUTPUT, i}) == 0) {
+ LOG(ERROR) << "AidlManagedBuffer::validateRequest -- invalid buffer role.";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ auto combined = combineDimensions(kInitialDimensions, request.outputs[i].dimensions);
+ if (!combined.has_value()) {
+ LOG(ERROR) << "AidlManagedBuffer::validateRequest -- incompatible dimensions ("
+ << toString(kInitialDimensions) << " vs "
+ << toString(request.outputs[i].dimensions) << ")";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ usedAsOutput = true;
+ }
+ return ErrorStatus::NONE;
+}
+
+ErrorStatus AidlManagedBuffer::validateCopyFrom(const std::vector<uint32_t>& dimensions,
+ uint32_t size) const {
+ if (size != kSize) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- invalid memory size: " << kSize
+ << " vs " << size;
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+
+ if (isNonExtensionScalar(kOperandType)) {
+ if (!dimensions.empty()) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- invalid dimensions for scalar "
+ "operand: "
+ << toString(dimensions);
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ return ErrorStatus::NONE;
+ }
+
+ if (dimensions.empty()) {
+ if (tensorHasUnspecifiedDimensions(kOperandType, kInitialDimensions)) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- the initial dimensions are not "
+ "fully "
+ "specified and no dimension update is provided: "
+ << toString(kInitialDimensions);
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ } else {
+ if (tensorHasUnspecifiedDimensions(kOperandType, dimensions)) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- the updated dimensions are not "
+ "fully "
+ "specified: "
+ << toString(dimensions);
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ }
+
+ const auto combined = combineDimensions(kInitialDimensions, dimensions);
+ if (!combined.has_value()) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyFrom -- incompatible dimensions ("
+ << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")";
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ return ErrorStatus::NONE;
+}
+
+ErrorStatus AidlManagedBuffer::validateCopyTo(uint32_t size) const {
+ if (size != kSize) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyTo -- invalid memory size: " << kSize
+ << " vs " << size;
+ return ErrorStatus::INVALID_ARGUMENT;
+ }
+ std::lock_guard<std::mutex> guard(mMutex);
+ if (!mInitialized) {
+ LOG(ERROR) << "AidlManagedBuffer::validateCopyTo -- using uninitialized buffer as source.";
+ return ErrorStatus::GENERAL_FAILURE;
+ }
+ return ErrorStatus::NONE;
+}
+
+bool AidlManagedBuffer::updateDimensions(const std::vector<uint32_t>& dimensions) {
+ auto combined = combineDimensions(kInitialDimensions, dimensions);
+ if (!combined.has_value()) {
+ LOG(ERROR) << "AidlManagedBuffer::updateDimensions -- incompatible dimensions ("
+ << toString(kInitialDimensions) << " vs " << toString(dimensions) << ")";
+ return false;
+ }
+ std::lock_guard<std::mutex> guard(mMutex);
+ mUpdatedDimensions = std::move(combined).value();
+ return true;
+}
+
+void AidlManagedBuffer::setInitialized(bool initialized) {
+ std::lock_guard<std::mutex> guard(mMutex);
+ mInitialized = initialized;
+}
+
+std::unique_ptr<AidlBufferTracker::Token> AidlBufferTracker::add(
+ std::shared_ptr<AidlManagedBuffer> buffer) {
+ if (buffer == nullptr) {
+ return nullptr;
+ }
+ std::lock_guard<std::mutex> guard(mMutex);
+ uint32_t token = 0;
+ if (mFreeTokens.empty()) {
+ token = mTokenToBuffers.size();
+ mTokenToBuffers.push_back(std::move(buffer));
+ } else {
+ token = mFreeTokens.top();
+ mFreeTokens.pop();
+ mTokenToBuffers[token] = std::move(buffer);
+ }
+ VLOG(MEMORY) << "AidlBufferTracker::add -- new token = " << token;
+ return std::make_unique<Token>(token, shared_from_this());
+}
+
+std::shared_ptr<AidlManagedBuffer> AidlBufferTracker::get(uint32_t token) const {
+ std::lock_guard<std::mutex> guard(mMutex);
+ if (mTokenToBuffers.size() <= token || mTokenToBuffers[token] == nullptr) {
+ LOG(ERROR) << "AidlBufferTracker::get -- unknown token " << token;
+ return nullptr;
+ }
+ return mTokenToBuffers[token];
+}
+
+void AidlBufferTracker::free(uint32_t token) {
+ std::lock_guard<std::mutex> guard(mMutex);
+ CHECK_LT(token, mTokenToBuffers.size());
+ CHECK(mTokenToBuffers[token] != nullptr);
+ VLOG(MEMORY) << "AidlBufferTracker::free -- release token = " << token;
+ mTokenToBuffers[token] = nullptr;
+ mFreeTokens.push(token);
+}
+
+} // namespace android::nn
diff --git a/neuralnetworks/aidl/utils/src/AidlHalUtils.cpp b/neuralnetworks/aidl/utils/src/AidlHalUtils.cpp
new file mode 100644
index 0000000..6fc46ab
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/AidlHalUtils.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+// This file contains pre-canonical-types utility code and includes HAL
+// utilities. LegacyUtils.h is the subset of these utilities that do not touch
+// HAL.
+
+#include "AidlHalUtils.h"
+
+#include <android-base/logging.h>
+#include <nnapi/hal/aidl/Conversions.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <vector>
+
+#include "AidlHalInterfaces.h"
+#include "nnapi/TypeUtils.h"
+
+namespace android::nn {
+
+std::vector<aidl_hal::OperandPerformance> nonExtensionOperandPerformance(
+ aidl_hal::PerformanceInfo perf) {
+ static constexpr ndk::enum_range<aidl_hal::OperandType> kOperandTypeRange;
+ std::vector<aidl_hal::OperandPerformance> ret;
+ ret.reserve(std::distance(kOperandTypeRange.begin(), kOperandTypeRange.end()));
+ for (aidl_hal::OperandType type : kOperandTypeRange) {
+ if (type != aidl_hal::OperandType::SUBGRAPH) {
+ ret.push_back(aidl_hal::OperandPerformance{type, perf});
+ }
+ }
+ std::sort(ret.begin(), ret.end(),
+ [](const aidl_hal::OperandPerformance& a, const aidl_hal::OperandPerformance& b) {
+ return a.type < b.type;
+ });
+
+ return ret;
+}
+
+void update(std::vector<aidl_hal::OperandPerformance>* operandPerformance,
+ aidl_hal::OperandType type, aidl_hal::PerformanceInfo perf) {
+ CHECK(operandPerformance != nullptr);
+ const auto it = std::lower_bound(operandPerformance->begin(), operandPerformance->end(), type,
+ [](const aidl_hal::OperandPerformance& perf,
+ aidl_hal::OperandType type) { return perf.type < type; });
+ CHECK(it != operandPerformance->end())
+ << toString(type) << " not in operand performance vector";
+ it->info = perf;
+}
+
+bool isExtensionOperandType(aidl_hal::OperandType type) {
+ return isExtension(convert(type).value());
+}
+
+bool isNonExtensionScalar(aidl_hal::OperandType type) {
+ return isNonExtensionScalar(convert(type).value());
+}
+
+} // namespace android::nn
diff --git a/neuralnetworks/aidl/utils/src/AidlValidateHal.cpp b/neuralnetworks/aidl/utils/src/AidlValidateHal.cpp
new file mode 100644
index 0000000..a1c496a
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/AidlValidateHal.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.
+ */
+
+#define LOG_TAG "ValidateHal"
+
+#include "AidlValidateHal.h"
+
+#include <android-base/logging.h>
+#include <nnapi/hal/aidl/Conversions.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "AidlHalUtils.h"
+#include "nnapi/TypeUtils.h"
+
+namespace android {
+namespace nn {
+
+bool validateMemoryDesc(
+ const aidl_hal::BufferDesc& desc,
+ const std::vector<std::shared_ptr<aidl_hal::IPreparedModel>>& preparedModels,
+ const std::vector<aidl_hal::BufferRole>& inputRoles,
+ const std::vector<aidl_hal::BufferRole>& outputRoles,
+ std::function<const aidl_hal::Model*(const std::shared_ptr<aidl_hal::IPreparedModel>&)>
+ getModel,
+ std::set<AidlHalPreparedModelRole>* preparedModelRoles,
+ aidl_hal::Operand* combinedOperand) {
+ NN_RET_CHECK(!preparedModels.empty());
+ NN_RET_CHECK(!inputRoles.empty() || !outputRoles.empty());
+
+ std::set<AidlHalPreparedModelRole> roles;
+ std::vector<aidl_hal::Operand> operands;
+ operands.reserve(inputRoles.size() + outputRoles.size());
+ for (const auto& role : inputRoles) {
+ NN_RET_CHECK_GE(role.modelIndex, 0);
+ NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size());
+ const auto& preparedModel = preparedModels[role.modelIndex];
+ NN_RET_CHECK(preparedModel != nullptr);
+ const auto* model = getModel(preparedModel);
+ NN_RET_CHECK(model != nullptr);
+ const auto& inputIndexes = model->main.inputIndexes;
+ NN_RET_CHECK_GE(role.ioIndex, 0);
+ NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), inputIndexes.size());
+ NN_RET_CHECK_GT(role.probability, 0.0f);
+ NN_RET_CHECK_LE(role.probability, 1.0f);
+ const auto [it, success] = roles.emplace(preparedModel.get(), IOType::INPUT, role.ioIndex);
+ NN_RET_CHECK(success);
+ operands.push_back(model->main.operands[inputIndexes[role.ioIndex]]);
+ }
+ for (const auto& role : outputRoles) {
+ NN_RET_CHECK_GE(role.modelIndex, 0);
+ NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size());
+ const auto& preparedModel = preparedModels[role.modelIndex];
+ NN_RET_CHECK(preparedModel != nullptr);
+ const auto* model = getModel(preparedModel);
+ NN_RET_CHECK(model != nullptr);
+ const auto& outputIndexes = model->main.outputIndexes;
+ NN_RET_CHECK_GE(role.ioIndex, 0);
+ NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), outputIndexes.size());
+ NN_RET_CHECK_GT(role.probability, 0.0f);
+ NN_RET_CHECK_LE(role.probability, 1.0f);
+ const auto [it, success] = roles.emplace(preparedModel.get(), IOType::OUTPUT, role.ioIndex);
+ NN_RET_CHECK(success);
+ operands.push_back(model->main.operands[outputIndexes[role.ioIndex]]);
+ }
+
+ CHECK(!operands.empty());
+ const auto opType = operands[0].type;
+ const auto canonicalOperandType = convert(opType);
+ NN_RET_CHECK(canonicalOperandType.has_value()) << canonicalOperandType.error().message;
+ const bool isExtensionOperand = isExtension(canonicalOperandType.value());
+
+ auto maybeDimensions = toUnsigned(desc.dimensions);
+ NN_RET_CHECK(maybeDimensions.has_value()) << maybeDimensions.error().message;
+ std::vector<uint32_t> dimensions = std::move(maybeDimensions).value();
+
+ for (const auto& operand : operands) {
+ NN_RET_CHECK(operand.type == operands[0].type)
+ << toString(operand.type) << " vs " << toString(operands[0].type);
+ NN_RET_CHECK_EQ(operand.scale, operands[0].scale);
+ NN_RET_CHECK_EQ(operand.zeroPoint, operands[0].zeroPoint);
+ // NOTE: validateMemoryDesc cannot validate extra parameters for extension operand type.
+ if (!isExtensionOperand) {
+ const auto& lhsExtraParams = operand.extraParams;
+ const auto& rhsExtraParams = operands[0].extraParams;
+ NN_RET_CHECK(lhsExtraParams == rhsExtraParams)
+ << (lhsExtraParams.has_value() ? lhsExtraParams.value().toString()
+ : "std::nullopt")
+ << " vs "
+ << (rhsExtraParams.has_value() ? rhsExtraParams.value().toString()
+ : "std::nullopt");
+ }
+ const auto maybeRhsDimensions = toUnsigned(operand.dimensions);
+ NN_RET_CHECK(maybeRhsDimensions.has_value()) << maybeRhsDimensions.error().message;
+ const auto combined = combineDimensions(dimensions, maybeRhsDimensions.value());
+ NN_RET_CHECK(combined.has_value());
+ dimensions = combined.value();
+ }
+
+ // NOTE: validateMemoryDesc cannot validate scalar dimensions with extension operand type.
+ if (!isExtensionOperand) {
+ NN_RET_CHECK(!isNonExtensionScalar(opType) || dimensions.empty())
+ << "invalid dimensions with scalar operand type.";
+ }
+
+ if (preparedModelRoles != nullptr) {
+ *preparedModelRoles = std::move(roles);
+ }
+ if (combinedOperand != nullptr) {
+ *combinedOperand = operands[0];
+ // No need to check that values fit int32_t here, since the original values are obtained
+ // from int32_t.
+ combinedOperand->dimensions = aidl_hal::utils::toSigned(dimensions).value();
+ }
+ return true;
+}
+
+} // namespace nn
+} // namespace android
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
index c59c10b..fb00b26 100644
--- a/neuralnetworks/aidl/utils/src/Burst.cpp
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -26,7 +26,6 @@
#include <nnapi/Result.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/HandleError.h>
#include <memory>
#include <mutex>
diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp
index 8055665..a321477 100644
--- a/neuralnetworks/aidl/utils/src/Callbacks.cpp
+++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp
@@ -38,7 +38,7 @@
// nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError.
nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
- HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+ HANDLE_STATUS_AIDL(status) << "model preparation failed with " << toString(status);
return NN_TRY(PreparedModel::create(preparedModel));
}
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index ddff3f2..45628c8 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -34,7 +34,6 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <algorithm>
#include <chrono>
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
index e80de0b..5b7ec4e 100644
--- a/neuralnetworks/aidl/utils/src/Device.cpp
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -125,7 +125,7 @@
} // namespace
nn::GeneralResult<std::shared_ptr<const Device>> Device::create(
- std::string name, std::shared_ptr<aidl_hal::IDevice> device) {
+ std::string name, std::shared_ptr<aidl_hal::IDevice> device, nn::Version featureLevel) {
if (name.empty()) {
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
<< "aidl_hal::utils::Device::create must have non-empty name";
@@ -143,18 +143,19 @@
auto deathHandler = NN_TRY(DeathHandler::create(device));
return std::make_shared<const Device>(
- PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
- std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+ PrivateConstructorTag{}, std::move(name), std::move(versionString), featureLevel,
+ deviceType, std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
std::move(device), std::move(deathHandler));
}
Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
- nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
- nn::Capabilities capabilities,
+ nn::Version featureLevel, nn::DeviceType deviceType,
+ std::vector<nn::Extension> extensions, nn::Capabilities capabilities,
std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler)
: kName(std::move(name)),
kVersionString(std::move(versionString)),
+ kFeatureLevel(featureLevel),
kDeviceType(deviceType),
kExtensions(std::move(extensions)),
kCapabilities(std::move(capabilities)),
@@ -171,7 +172,7 @@
}
nn::Version Device::getFeatureLevel() const {
- return nn::Version::ANDROID_S;
+ return kFeatureLevel;
}
nn::DeviceType Device::getType() const {
diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp
index 13d4f32..94edd90 100644
--- a/neuralnetworks/aidl/utils/src/Execution.cpp
+++ b/neuralnetworks/aidl/utils/src/Execution.cpp
@@ -25,7 +25,6 @@
#include <nnapi/Result.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <memory>
#include <utility>
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
index 0769016..f25c2c8 100644
--- a/neuralnetworks/aidl/utils/src/PreparedModel.cpp
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -30,7 +30,6 @@
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
#include <nnapi/hal/CommonUtils.h>
-#include <nnapi/hal/HandleError.h>
#include <memory>
#include <tuple>
@@ -51,7 +50,7 @@
nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResults(
ErrorStatus status, const aidl_hal::Timing& timingLaunched,
const aidl_hal::Timing& timingFenced) {
- HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+ HANDLE_STATUS_AIDL(status) << "fenced execution callback info failed with " << toString(status);
return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
}
diff --git a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
index 124641c..54a673c 100644
--- a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
+++ b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
@@ -22,7 +22,6 @@
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
#include <nnapi/Result.h>
-#include <nnapi/hal/ProtectCallback.h>
#include <algorithm>
#include <functional>
@@ -37,7 +36,7 @@
void DeathMonitor::serviceDied() {
std::lock_guard guard(mMutex);
std::for_each(mObjects.begin(), mObjects.end(),
- [](hal::utils::IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+ [](IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
}
void DeathMonitor::serviceDied(void* cookie) {
@@ -45,13 +44,13 @@
deathMonitor->serviceDied();
}
-void DeathMonitor::add(hal::utils::IProtectedCallback* killable) const {
+void DeathMonitor::add(IProtectedCallback* killable) const {
CHECK(killable != nullptr);
std::lock_guard guard(mMutex);
mObjects.push_back(killable);
}
-void DeathMonitor::remove(hal::utils::IProtectedCallback* killable) const {
+void DeathMonitor::remove(IProtectedCallback* killable) const {
CHECK(killable != nullptr);
std::lock_guard guard(mMutex);
const auto removedIter = std::remove(mObjects.begin(), mObjects.end(), killable);
@@ -102,7 +101,7 @@
}
[[nodiscard]] ::android::base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
- hal::utils::IProtectedCallback* killable) const {
+ IProtectedCallback* killable) const {
CHECK(killable != nullptr);
kDeathMonitor->add(killable);
return ::android::base::make_scope_guard(
diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp
index ac182a2..01772ee 100644
--- a/neuralnetworks/aidl/utils/src/Service.cpp
+++ b/neuralnetworks/aidl/utils/src/Service.cpp
@@ -17,6 +17,7 @@
#include "Service.h"
#include <AndroidVersionUtil.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
#include <android/binder_auto_utils.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
@@ -28,8 +29,33 @@
#include <string>
#include "Device.h"
+#include "Utils.h"
namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+// Map the AIDL version of an IDevice to NNAPI canonical feature level.
+nn::GeneralResult<nn::Version> getAidlServiceFeatureLevel(IDevice* service) {
+ CHECK(service != nullptr);
+ int aidlVersion;
+ const auto ret = service->getInterfaceVersion(&aidlVersion);
+ HANDLE_ASTATUS(ret) << "getInterfaceVersion failed";
+
+ // For service AIDL versions greater than or equal to the AIDL library version that the runtime
+ // was built against, clamp it to the runtime AIDL library version.
+ aidlVersion = std::min(aidlVersion, IDevice::version);
+
+ // Map stable AIDL versions to canonical versions.
+ switch (aidlVersion) {
+ case 1:
+ return nn::Version::ANDROID_S;
+ case 2:
+ return nn::Version::FEATURE_LEVEL_6;
+ }
+ return NN_ERROR() << "Unknown AIDL service version: " << aidlVersion;
+}
+
+} // namespace
nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) {
auto fullName = std::string(IDevice::descriptor) + "/" + instanceName;
@@ -55,7 +81,8 @@
<< " returned nullptr";
}
ABinderProcess_startThreadPool();
- return Device::create(instanceName, std::move(service));
+ const auto featureLevel = NN_TRY(getAidlServiceFeatureLevel(service.get()));
+ return Device::create(instanceName, std::move(service), featureLevel);
};
return hal::utils::ResilientDevice::create(std::move(makeDevice));
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
index f121aca..79abe1b 100644
--- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -146,28 +146,45 @@
return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
};
+class DeviceTest : public ::testing::TestWithParam<nn::Version> {
+ protected:
+ const nn::Version kVersion = GetParam();
+};
+
+std::string printDeviceTest(const testing::TestParamInfo<nn::Version>& info) {
+ switch (info.param) {
+ case nn::Version::ANDROID_S:
+ return "v1";
+ case nn::Version::FEATURE_LEVEL_6:
+ return "v2";
+ default:
+ LOG(FATAL) << "Invalid AIDL version: " << info.param;
+ return "invalid";
+ }
+}
+
} // namespace
-TEST(DeviceTest, invalidName) {
+TEST_P(DeviceTest, invalidName) {
// run test
const auto device = MockDevice::create();
- const auto result = Device::create(kInvalidName, device);
+ const auto result = Device::create(kInvalidName, device, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
}
-TEST(DeviceTest, invalidDevice) {
+TEST_P(DeviceTest, invalidDevice) {
// run test
- const auto result = Device::create(kName, kInvalidDevice);
+ const auto result = Device::create(kName, kInvalidDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
}
-TEST(DeviceTest, getVersionStringError) {
+TEST_P(DeviceTest, getVersionStringError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -175,14 +192,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getVersionStringTransportFailure) {
+TEST_P(DeviceTest, getVersionStringTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -190,14 +207,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getVersionStringDeadObject) {
+TEST_P(DeviceTest, getVersionStringDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_))
@@ -205,27 +222,27 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getTypeError) {
+TEST_P(DeviceTest, getTypeError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getTypeTransportFailure) {
+TEST_P(DeviceTest, getTypeTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_))
@@ -233,14 +250,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getTypeDeadObject) {
+TEST_P(DeviceTest, getTypeDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getType(_))
@@ -248,14 +265,14 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getSupportedExtensionsError) {
+TEST_P(DeviceTest, getSupportedExtensionsError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -263,14 +280,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+TEST_P(DeviceTest, getSupportedExtensionsTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -278,14 +295,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+TEST_P(DeviceTest, getSupportedExtensionsDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
@@ -293,20 +310,20 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeeded) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeeded) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_TRUE(result.has_value());
@@ -315,7 +332,7 @@
EXPECT_EQ(result.value()->getNumberOfCacheFilesNeeded(), kNumberOfCacheFilesPair);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -323,14 +340,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+TEST_P(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
// setup test
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -341,14 +358,14 @@
InvokeWithoutArgs(makeStatusOk)));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+TEST_P(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
// setup test
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -359,14 +376,14 @@
InvokeWithoutArgs(makeStatusOk)));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -374,14 +391,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+TEST_P(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
@@ -389,14 +406,14 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getCapabilitiesError) {
+TEST_P(DeviceTest, getCapabilitiesError) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -404,14 +421,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getCapabilitiesTransportFailure) {
+TEST_P(DeviceTest, getCapabilitiesTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -419,14 +436,14 @@
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getCapabilitiesDeadObject) {
+TEST_P(DeviceTest, getCapabilitiesDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getCapabilities(_))
@@ -434,17 +451,17 @@
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
// run test
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
// verify result
ASSERT_FALSE(result.has_value());
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, getName) {
+TEST_P(DeviceTest, getName) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
// run test
const auto& name = device->getName();
@@ -453,19 +470,19 @@
EXPECT_EQ(name, kName);
}
-TEST(DeviceTest, getFeatureLevel) {
+TEST_P(DeviceTest, getFeatureLevel) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
// run test
const auto featureLevel = device->getFeatureLevel();
// verify result
- EXPECT_EQ(featureLevel, nn::Version::ANDROID_S);
+ EXPECT_EQ(featureLevel, kVersion);
}
-TEST(DeviceTest, getCachedData) {
+TEST_P(DeviceTest, getCachedData) {
// setup call
const auto mockDevice = createMockDevice();
EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
@@ -474,7 +491,7 @@
EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1);
- const auto result = Device::create(kName, mockDevice);
+ const auto result = Device::create(kName, mockDevice, kVersion);
ASSERT_TRUE(result.has_value())
<< "Failed with " << result.error().code << ": " << result.error().message;
const auto& device = result.value();
@@ -487,10 +504,10 @@
EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
}
-TEST(DeviceTest, getSupportedOperations) {
+TEST_P(DeviceTest, getSupportedOperations) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(DoAll(
@@ -508,10 +525,10 @@
EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
}
-TEST(DeviceTest, getSupportedOperationsError) {
+TEST_P(DeviceTest, getSupportedOperationsError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
@@ -524,10 +541,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+TEST_P(DeviceTest, getSupportedOperationsTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -540,10 +557,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, getSupportedOperationsDeadObject) {
+TEST_P(DeviceTest, getSupportedOperationsDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -556,10 +573,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModel) {
+TEST_P(DeviceTest, prepareModel) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockPreparedModel = MockPreparedModel::create();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
@@ -576,10 +593,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelLaunchError) {
+TEST_P(DeviceTest, prepareModelLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::GENERAL_FAILURE,
@@ -594,10 +611,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelReturnError) {
+TEST_P(DeviceTest, prepareModelReturnError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE,
@@ -612,10 +629,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelNullptrError) {
+TEST_P(DeviceTest, prepareModelNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(
@@ -630,10 +647,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelTransportFailure) {
+TEST_P(DeviceTest, prepareModelTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -647,10 +664,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelDeadObject) {
+TEST_P(DeviceTest, prepareModelDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -664,10 +681,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelAsyncCrash) {
+TEST_P(DeviceTest, prepareModelAsyncCrash) {
// setup test
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto ret = [&device]() {
DeathMonitor::serviceDied(device->getDeathMonitor());
return ndk::ScopedAStatus::ok();
@@ -685,10 +702,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelFromCache) {
+TEST_P(DeviceTest, prepareModelFromCache) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockPreparedModel = MockPreparedModel::create();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
@@ -704,10 +721,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+TEST_P(DeviceTest, prepareModelFromCacheLaunchError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(
@@ -721,10 +738,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheReturnError) {
+TEST_P(DeviceTest, prepareModelFromCacheReturnError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(
@@ -738,10 +755,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+TEST_P(DeviceTest, prepareModelFromCacheNullptrError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
@@ -755,10 +772,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+TEST_P(DeviceTest, prepareModelFromCacheTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -771,10 +788,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+TEST_P(DeviceTest, prepareModelFromCacheDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -787,10 +804,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+TEST_P(DeviceTest, prepareModelFromCacheAsyncCrash) {
// setup test
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto ret = [&device]() {
DeathMonitor::serviceDied(device->getDeathMonitor());
return ndk::ScopedAStatus::ok();
@@ -807,10 +824,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
-TEST(DeviceTest, allocate) {
+TEST_P(DeviceTest, allocate) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
const auto mockBuffer = DeviceBuffer{.buffer = MockBuffer::create(), .token = 1};
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
@@ -825,10 +842,10 @@
EXPECT_NE(result.value(), nullptr);
}
-TEST(DeviceTest, allocateError) {
+TEST_P(DeviceTest, allocateError) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralFailure));
@@ -841,10 +858,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, allocateTransportFailure) {
+TEST_P(DeviceTest, allocateTransportFailure) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
@@ -857,10 +874,10 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
}
-TEST(DeviceTest, allocateDeadObject) {
+TEST_P(DeviceTest, allocateDeadObject) {
// setup call
const auto mockDevice = createMockDevice();
- const auto device = Device::create(kName, mockDevice).value();
+ const auto device = Device::create(kName, mockDevice, kVersion).value();
EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
@@ -873,4 +890,8 @@
EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
}
+INSTANTIATE_TEST_SUITE_P(TestDevice, DeviceTest,
+ ::testing::Values(nn::Version::ANDROID_S, nn::Version::FEATURE_LEVEL_6),
+ printDeviceTest);
+
} // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index 4dc2ed0..a102fe0 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -26,6 +26,7 @@
cc_test {
name: "VtsHalNeuralnetworksTargetTest",
defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
"neuralnetworks_vts_functional_defaults",
"use_libaidlvintf_gtest_helper_static",
],
@@ -49,20 +50,17 @@
"libvndksupport",
],
static_libs: [
- "android.hardware.common-V2-ndk",
- "android.hardware.graphics.common-V2-ndk",
- "android.hardware.neuralnetworks-V2-ndk",
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libaidlcommonsupport",
"libgmock",
"libhidlmemory",
+ "libneuralnetworks_common",
"libneuralnetworks_generated_test_harness",
- "libneuralnetworks_utils",
"libsync",
- "neuralnetworks_utils_hal_aidl",
],
whole_static_libs: [
+ "neuralnetworks_generated_AIDL_V2_example",
"neuralnetworks_generated_V1_0_example",
"neuralnetworks_generated_V1_1_example",
"neuralnetworks_generated_V1_2_example",
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index ac5b96a..f67fd34 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -907,6 +907,20 @@
const bool deviceIsResponsive =
ndk::ScopedAStatus::fromStatus(AIBinder_ping(kDevice->asBinder().get())).isOk();
ASSERT_TRUE(deviceIsResponsive);
+ // TODO(b/201260787): We should require old drivers to report the model as
+ // unsupported instead of simply skipping the test.
+ SkipIfDriverOlderThanTestModel();
+}
+
+void GeneratedTestBase::SkipIfDriverOlderThanTestModel() {
+ int32_t deviceVersion;
+ ASSERT_TRUE(kDevice->getInterfaceVersion(&deviceVersion).isOk());
+ const int32_t modelVersion = kTestModel.getAidlVersionInt();
+ if (deviceVersion < modelVersion) {
+ GTEST_SKIP() << "Device interface version " << deviceVersion
+ << " is older than test model's minimum supported HAL version " << modelVersion
+ << ". Skipping test.";
+ }
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
index ad40f06..da74db9 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
@@ -34,6 +34,9 @@
void SetUp() override;
const std::shared_ptr<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+
+ private:
+ void SkipIfDriverOlderThanTestModel();
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
index 698c054..fdc7eff 100644
--- a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -1122,6 +1122,7 @@
// align_corners and half_pixel_centers parameters.
// - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
// parameter.
+ // - PACK has at least 2 inputs, with the first element being INT32.
switch (op.type) {
case OperationType::CONCATENATION: {
if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
@@ -1178,6 +1179,11 @@
return true;
}
} break;
+ case OperationType::PACK: {
+ if (op.inputs.size() > 2 && input != 0) {
+ return true;
+ }
+ } break;
default:
break;
}
@@ -1315,8 +1321,8 @@
void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
const auto numberOfConsumers =
- nn::countNumberOfConsumers(model.main.operands.size(),
- nn::unvalidatedConvert(model.main.operations).value())
+ countNumberOfConsumers(model.main.operands.size(),
+ nn::unvalidatedConvert(model.main.operations).value())
.value();
mutateExecutionOrderTest(device, model, numberOfConsumers);
mutateOperandTypeTest(device, model);
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h b/neuralnetworks/utils/adapter/include/nnapi/hal/Burst.h
similarity index 65%
rename from neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
rename to neuralnetworks/utils/adapter/include/nnapi/hal/Burst.h
index f7926f5..a3aa706 100644
--- a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
+++ b/neuralnetworks/utils/adapter/include/nnapi/hal/Burst.h
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
-#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
-
-#include "ExecutionBurstUtils.h"
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BURST_H
#include <android-base/thread_annotations.h>
#include <android/hardware/neuralnetworks/1.0/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>
@@ -29,7 +28,8 @@
#include <nnapi/IBurst.h>
#include <nnapi/Result.h>
#include <nnapi/Types.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
+#include <nnapi/hal/1.2/BurstUtils.h>
#include <atomic>
#include <chrono>
@@ -39,13 +39,13 @@
#include <tuple>
#include <vector>
-namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace android::hardware::neuralnetworks::adapter {
/**
- * 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.
+ * The Burst 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 IBurstContext {
+class Burst : public V1_2::IBurstContext {
struct PrivateConstructorTag {};
public:
@@ -58,13 +58,13 @@
public:
// Precondition: burstExecutor != nullptr
// Precondition: burstCallback != nullptr
- MemoryCache(nn::SharedBurst burstExecutor, sp<IBurstCallback> burstCallback);
+ MemoryCache(nn::SharedBurst burstExecutor, sp<V1_2::IBurstCallback> burstCallback);
/**
* Get the cached memory objects corresponding to provided slot identifiers.
*
- * If the slot entry is not present in the cache, this class will use IBurstCallback to
- * retrieve those entries that are not present in the cache, then cache them.
+ * If the slot entry is not present in the cache, this class will use V1_2::IBurstCallback
+ * to retrieve those entries that are not present in the cache, then cache them.
*
* @param slots Identifiers of memory objects to be retrieved.
* @return A vector where each element is the memory object and a ref-counted cache "hold"
@@ -93,7 +93,7 @@
std::map<int32_t, std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> mCache
GUARDED_BY(mMutex);
nn::SharedBurst kBurstExecutor;
- const sp<IBurstCallback> kBurstCallback;
+ const sp<V1_2::IBurstCallback> kBurstCallback;
};
/**
@@ -111,45 +111,45 @@
* execution.
* @param burstExecutor 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.
- * @return IBurstContext Handle to the burst context.
+ * @param pollingTimeWindow How much time (in microseconds) the Burst 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 V1_2::IBurstContext Handle to the burst context.
*/
- static nn::GeneralResult<sp<ExecutionBurstServer>> create(
- const sp<IBurstCallback>& callback,
- const MQDescriptorSync<FmqRequestDatum>& requestChannel,
- const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
+ static nn::GeneralResult<sp<Burst>> create(
+ const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+ nn::SharedBurst burstExecutor,
std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
- ExecutionBurstServer(PrivateConstructorTag tag, const sp<IBurstCallback>& callback,
- std::unique_ptr<RequestChannelReceiver> requestChannel,
- std::unique_ptr<ResultChannelSender> resultChannel,
- nn::SharedBurst burstExecutor);
- ~ExecutionBurstServer();
+ Burst(PrivateConstructorTag tag, const sp<V1_2::IBurstCallback>& callback,
+ std::unique_ptr<V1_2::utils::RequestChannelReceiver> requestChannel,
+ std::unique_ptr<V1_2::utils::ResultChannelSender> resultChannel,
+ nn::SharedBurst burstExecutor);
+ ~Burst();
// Used by the NN runtime to preemptively remove any stored memory. See
- // IBurstContext::freeMemory for more information.
+ // V1_2::IBurstContext::freeMemory for more information.
Return<void> freeMemory(int32_t slot) override;
private:
- // Work loop that will continue processing execution requests until the ExecutionBurstServer
- // object is freed.
+ // Work loop that will continue processing execution requests until the Burst object is freed.
void task();
- nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> execute(
+ nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> execute(
const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
- MeasureTiming measure);
+ V1_2::MeasureTiming measure);
std::thread mWorker;
std::atomic<bool> mTeardown{false};
- const sp<IBurstCallback> mCallback;
- const std::unique_ptr<RequestChannelReceiver> mRequestChannelReceiver;
- const std::unique_ptr<ResultChannelSender> mResultChannelSender;
+ const sp<V1_2::IBurstCallback> mCallback;
+ const std::unique_ptr<V1_2::utils::RequestChannelReceiver> mRequestChannelReceiver;
+ const std::unique_ptr<V1_2::utils::ResultChannelSender> mResultChannelSender;
const nn::SharedBurst mBurstExecutor;
MemoryCache mMemoryCache;
};
-} // namespace android::hardware::neuralnetworks::V1_2::utils
+} // namespace android::hardware::neuralnetworks::adapter
-#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
+#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_ADAPTER_BURST_H
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/utils/adapter/src/Burst.cpp
similarity index 65%
rename from neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
rename to neuralnetworks/utils/adapter/src/Burst.cpp
index 65ec7f5..8b2e1dd 100644
--- a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
+++ b/neuralnetworks/utils/adapter/src/Burst.cpp
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "ExecutionBurstServer"
-
-#include "ExecutionBurstServer.h"
-#include "Conversions.h"
-#include "ExecutionBurstUtils.h"
+#include "Burst.h"
#include <android-base/logging.h>
#include <nnapi/IBurst.h>
@@ -27,8 +23,10 @@
#include <nnapi/Types.h>
#include <nnapi/Validation.h>
#include <nnapi/hal/1.0/Conversions.h>
-#include <nnapi/hal/HandleError.h>
-#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/1.0/HandleError.h>
+#include <nnapi/hal/1.0/ProtectCallback.h>
+#include <nnapi/hal/1.2/BurstUtils.h>
+#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/TransferValue.h>
#include <algorithm>
@@ -42,15 +40,15 @@
#include "Tracing.h"
-namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace android::hardware::neuralnetworks::adapter {
namespace {
-constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
- std::numeric_limits<uint64_t>::max()};
+constexpr V1_2::Timing kTiming = {std::numeric_limits<uint64_t>::max(),
+ std::numeric_limits<uint64_t>::max()};
nn::GeneralResult<std::vector<nn::SharedMemory>> getMemoriesCallback(
V1_0::ErrorStatus status, const hidl_vec<hidl_memory>& memories) {
- HANDLE_HAL_STATUS(status) << "getting burst memories failed with " << toString(status);
+ HANDLE_STATUS_HIDL(status) << "getting burst memories failed with " << toString(status);
std::vector<nn::SharedMemory> canonicalMemories;
canonicalMemories.reserve(memories.size());
for (const auto& memory : memories) {
@@ -61,15 +59,15 @@
} // anonymous namespace
-ExecutionBurstServer::MemoryCache::MemoryCache(nn::SharedBurst burstExecutor,
- sp<IBurstCallback> burstCallback)
+Burst::MemoryCache::MemoryCache(nn::SharedBurst burstExecutor,
+ sp<V1_2::IBurstCallback> burstCallback)
: kBurstExecutor(std::move(burstExecutor)), kBurstCallback(std::move(burstCallback)) {
CHECK(kBurstExecutor != nullptr);
CHECK(kBurstCallback != nullptr);
}
nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
-ExecutionBurstServer::MemoryCache::getCacheEntries(const std::vector<int32_t>& slots) {
+Burst::MemoryCache::getCacheEntries(const std::vector<int32_t>& slots) {
std::lock_guard guard(mMutex);
NN_TRY(ensureCacheEntriesArePresentLocked(slots));
@@ -82,7 +80,7 @@
return results;
}
-nn::GeneralResult<void> ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked(
+nn::GeneralResult<void> Burst::MemoryCache::ensureCacheEntriesArePresentLocked(
const std::vector<int32_t>& slots) {
const auto slotIsKnown = [this](int32_t slot)
REQUIRES(mMutex) { return mCache.count(slot) > 0; };
@@ -107,11 +105,10 @@
auto returnedMemories = NN_TRY(cb.take());
if (returnedMemories.size() != unknownSlots.size()) {
- return NN_ERROR()
- << "ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked: Error "
- "retrieving memories -- count mismatch between requested memories ("
- << unknownSlots.size() << ") and returned memories (" << returnedMemories.size()
- << ")";
+ return NN_ERROR() << "Burst::MemoryCache::ensureCacheEntriesArePresentLocked: Error "
+ "retrieving memories -- count mismatch between requested memories ("
+ << unknownSlots.size() << ") and returned memories ("
+ << returnedMemories.size() << ")";
}
// add memories to unknown slots
@@ -123,56 +120,54 @@
}
nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
-ExecutionBurstServer::MemoryCache::getCacheEntryLocked(int32_t slot) {
+Burst::MemoryCache::getCacheEntryLocked(int32_t slot) {
if (const auto iter = mCache.find(slot); iter != mCache.end()) {
return iter->second;
}
- return NN_ERROR()
- << "ExecutionBurstServer::MemoryCache::getCacheEntryLocked failed because slot " << slot
- << " is not present in the cache";
+ return NN_ERROR() << "Burst::MemoryCache::getCacheEntryLocked failed because slot " << slot
+ << " is not present in the cache";
}
-void ExecutionBurstServer::MemoryCache::addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) {
+void Burst::MemoryCache::addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) {
auto hold = kBurstExecutor->cacheMemory(memory);
mCache.emplace(slot, std::make_pair(std::move(memory), std::move(hold)));
}
-void ExecutionBurstServer::MemoryCache::removeCacheEntry(int32_t slot) {
+void Burst::MemoryCache::removeCacheEntry(int32_t slot) {
std::lock_guard guard(mMutex);
mCache.erase(slot);
}
-// ExecutionBurstServer methods
+// Burst methods
-nn::GeneralResult<sp<ExecutionBurstServer>> ExecutionBurstServer::create(
- const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
- const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
+nn::GeneralResult<sp<Burst>> Burst::create(
+ const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
std::chrono::microseconds pollingTimeWindow) {
// check inputs
if (callback == nullptr || burstExecutor == nullptr) {
- return NN_ERROR() << "ExecutionBurstServer::create passed a nullptr";
+ return NN_ERROR() << "Burst::create passed a nullptr";
}
// create FMQ objects
auto requestChannelReceiver =
- NN_TRY(RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
- auto resultChannelSender = NN_TRY(ResultChannelSender::create(resultChannel));
+ NN_TRY(V1_2::utils::RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
+ auto resultChannelSender = NN_TRY(V1_2::utils::ResultChannelSender::create(resultChannel));
// check FMQ objects
CHECK(requestChannelReceiver != nullptr);
CHECK(resultChannelSender != nullptr);
// make and return context
- return sp<ExecutionBurstServer>::make(PrivateConstructorTag{}, callback,
- std::move(requestChannelReceiver),
- std::move(resultChannelSender), std::move(burstExecutor));
+ return sp<Burst>::make(PrivateConstructorTag{}, callback, std::move(requestChannelReceiver),
+ std::move(resultChannelSender), std::move(burstExecutor));
}
-ExecutionBurstServer::ExecutionBurstServer(PrivateConstructorTag /*tag*/,
- const sp<IBurstCallback>& callback,
- std::unique_ptr<RequestChannelReceiver> requestChannel,
- std::unique_ptr<ResultChannelSender> resultChannel,
- nn::SharedBurst burstExecutor)
+Burst::Burst(PrivateConstructorTag /*tag*/, const sp<V1_2::IBurstCallback>& callback,
+ std::unique_ptr<V1_2::utils::RequestChannelReceiver> requestChannel,
+ std::unique_ptr<V1_2::utils::ResultChannelSender> resultChannel,
+ nn::SharedBurst burstExecutor)
: mCallback(callback),
mRequestChannelReceiver(std::move(requestChannel)),
mResultChannelSender(std::move(resultChannel)),
@@ -182,7 +177,7 @@
mWorker = std::thread([this] { task(); });
}
-ExecutionBurstServer::~ExecutionBurstServer() {
+Burst::~Burst() {
// set teardown flag
mTeardown = true;
mRequestChannelReceiver->invalidate();
@@ -191,12 +186,12 @@
mWorker.join();
}
-Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
+Return<void> Burst::freeMemory(int32_t slot) {
mMemoryCache.removeCacheEntry(slot);
return Void();
}
-void ExecutionBurstServer::task() {
+void Burst::task() {
// loop until the burst object is being destroyed
while (!mTeardown) {
// receive request
@@ -208,12 +203,12 @@
// if the burst is being torn down, skip the execution so the "task" function can end
if (!arguments.has_value()) {
if (!mTeardown) {
- mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+ mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kTiming);
}
continue;
}
- // unpack the arguments; types are Request, std::vector<int32_t>, and MeasureTiming,
+ // unpack the arguments; types are Request, std::vector<int32_t>, and V1_2::MeasureTiming,
// respectively
const auto [requestWithoutPools, slotsOfPools, measure] = std::move(arguments).value();
@@ -226,17 +221,17 @@
} else {
const auto& [message, code, outputShapes] = result.error();
LOG(ERROR) << "IBurst::execute failed with " << code << ": " << message;
- mResultChannelSender->send(convert(code).value(), convert(outputShapes).value(),
- kNoTiming);
+ mResultChannelSender->send(V1_2::utils::convert(code).value(),
+ V1_2::utils::convert(outputShapes).value(), kTiming);
}
}
}
-nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> ExecutionBurstServer::execute(
+nn::ExecutionResult<std::pair<hidl_vec<V1_2::OutputShape>, V1_2::Timing>> Burst::execute(
const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
- MeasureTiming measure) {
+ V1_2::MeasureTiming measure) {
NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
- "ExecutionBurstServer getting memory, executing, and returning results");
+ "Burst getting memory, executing, and returning results");
// ensure executor with cache has required memory
const auto cacheEntries = NN_TRY(mMemoryCache.getCacheEntries(slotsOfPools));
@@ -257,7 +252,8 @@
const auto [outputShapes, timing] =
NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
- return std::make_pair(NN_TRY(convert(outputShapes)), NN_TRY(convert(timing)));
+ return std::make_pair(NN_TRY(V1_2::utils::convert(outputShapes)),
+ NN_TRY(V1_2::utils::convert(timing)));
}
-} // namespace android::hardware::neuralnetworks::V1_2::utils
+} // namespace android::hardware::neuralnetworks::adapter
diff --git a/neuralnetworks/utils/adapter/src/PreparedModel.cpp b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
index 7397def..a14e782 100644
--- a/neuralnetworks/utils/adapter/src/PreparedModel.cpp
+++ b/neuralnetworks/utils/adapter/src/PreparedModel.cpp
@@ -16,7 +16,8 @@
#include "PreparedModel.h"
-#include <ExecutionBurstServer.h>
+#include "Burst.h"
+
#include <android-base/logging.h>
#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
#include <android/hardware/neuralnetworks/1.0/types.h>
@@ -36,7 +37,6 @@
#include <nnapi/hal/1.2/Utils.h>
#include <nnapi/hal/1.3/Conversions.h>
#include <nnapi/hal/1.3/Utils.h>
-#include <nnapi/hal/HandleError.h>
#include <sys/types.h>
#include <memory>
@@ -273,6 +273,15 @@
return syncFences;
}
+nn::GeneralResult<sp<V1_2::IBurstContext>> configureExecutionBurst(
+ const nn::SharedPreparedModel& preparedModel, const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel) {
+ auto burstExecutor = NN_TRY(preparedModel->configureExecutionBurst());
+ return Burst::create(callback, requestChannel, resultChannel, std::move(burstExecutor),
+ V1_2::utils::getBurstServerPollingTimeWindow());
+}
+
nn::GeneralResult<std::pair<hidl_handle, sp<V1_3::IFencedExecutionCallback>>> executeFenced(
const nn::SharedPreparedModel& preparedModel, const V1_3::Request& request,
const hidl_vec<hidl_handle>& waitFor, V1_2::MeasureTiming measure,
@@ -389,14 +398,17 @@
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
configureExecutionBurst_cb cb) {
- const sp<V1_2::IBurstContext> burst = nn::ExecutionBurstServer::create(
- callback, requestChannel, resultChannel, this, std::chrono::microseconds{0});
-
- if (burst == nullptr) {
- cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
- } else {
- cb(V1_0::ErrorStatus::NONE, burst);
+ auto result = adapter::configureExecutionBurst(kPreparedModel, callback, requestChannel,
+ resultChannel);
+ if (!result.has_value()) {
+ auto [message, code] = std::move(result).error();
+ LOG(ERROR) << "adapter::PreparedModel::configureExecutionBurst failed with " << code << ": "
+ << message;
+ cb(V1_2::utils::convert(code).value(), nullptr);
+ return Void();
}
+ auto burstContext = std::move(result).value();
+ cb(V1_0::ErrorStatus::NONE, std::move(burstContext));
return Void();
}
diff --git a/neuralnetworks/utils/common/Android.bp b/neuralnetworks/utils/common/Android.bp
index f88e407..39927a3 100644
--- a/neuralnetworks/utils/common/Android.bp
+++ b/neuralnetworks/utils/common/Android.bp
@@ -30,20 +30,7 @@
local_include_dirs: ["include/nnapi/hal"],
export_include_dirs: ["include"],
cflags: ["-Wthread-safety"],
- static_libs: [
- "libarect",
- "neuralnetworks_types",
- ],
- shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
- "libhidlbase",
- "libbinder_ndk",
- ],
- target: {
- android: {
- shared_libs: ["libnativewindow"],
- },
- },
+ static_libs: ["neuralnetworks_types"],
}
cc_test {
@@ -51,7 +38,6 @@
host_supported: true,
srcs: ["test/*.cpp"],
static_libs: [
- "android.hardware.neuralnetworks@1.0",
"libgmock",
"libneuralnetworks_common",
"neuralnetworks_types",
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index 702ee92..ae0d092 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
-#include <cutils/native_handle.h>
-#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
#include <nnapi/SharedMemory.h>
#include <nnapi/Types.h>
@@ -125,18 +123,6 @@
const nn::Request* request, uint32_t alignment, uint32_t padding,
std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
-nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
- size_t numberOfOperands, const std::vector<nn::Operation>& operations);
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory);
-nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory);
-
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle);
-nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle);
-
-nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
- const std::vector<nn::SyncFence>& fences);
-
} // namespace android::hardware::neuralnetworks::utils
#endif // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index 235ba29..b249881 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -16,11 +16,7 @@
#include "CommonUtils.h"
-#include "HandleError.h"
-
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <hidl/HidlSupport.h>
#include <nnapi/Result.h>
#include <nnapi/SharedMemory.h>
#include <nnapi/TypeUtils.h>
@@ -34,11 +30,6 @@
#include <variant>
#include <vector>
-#ifdef __ANDROID__
-#include <android/hardware_buffer.h>
-#include <vndk/hardware_buffer.h>
-#endif // __ANDROID__
-
namespace android::hardware::neuralnetworks::utils {
namespace {
@@ -92,97 +83,6 @@
});
}
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(std::vector<base::unique_fd> fds,
- const std::vector<int32_t>& ints) {
- constexpr size_t kIntMax = std::numeric_limits<int>::max();
- CHECK_LE(fds.size(), kIntMax);
- CHECK_LE(ints.size(), kIntMax);
- native_handle_t* nativeHandle =
- native_handle_create(static_cast<int>(fds.size()), static_cast<int>(ints.size()));
- if (nativeHandle == nullptr) {
- return NN_ERROR() << "Failed to create native_handle";
- }
-
- for (size_t i = 0; i < fds.size(); ++i) {
- nativeHandle->data[i] = fds[i].release();
- }
- std::copy(ints.begin(), ints.end(), nativeHandle->data + nativeHandle->numFds);
-
- hidl_handle handle;
- handle.setTo(nativeHandle, /*shouldOwn=*/true);
- return handle;
-}
-
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
- const std::vector<int32_t>& ints) {
- std::vector<base::unique_fd> fds;
- fds.push_back(std::move(fd));
- return createNativeHandleFrom(std::move(fds), ints);
-}
-
-nn::GeneralResult<hidl_handle> createNativeHandleFrom(const nn::Memory::Unknown::Handle& handle) {
- std::vector<base::unique_fd> fds = NN_TRY(nn::dupFds(handle.fds.begin(), handle.fds.end()));
- return createNativeHandleFrom(std::move(fds), handle.ints);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
- auto fd = NN_TRY(nn::dupFd(memory.fd));
- auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
- return hidl_memory("ashmem", std::move(handle), memory.size);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
- auto fd = NN_TRY(nn::dupFd(memory.fd));
-
- const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
- const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
-
- auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
- return hidl_memory("mmap_fd", std::move(handle), memory.size);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
-#ifdef __ANDROID__
- const auto* ahwb = memory.handle.get();
- AHardwareBuffer_Desc bufferDesc;
- AHardwareBuffer_describe(ahwb, &bufferDesc);
-
- const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
- const size_t size = isBlob ? bufferDesc.width : 0;
- const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
-
- const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
- const hidl_handle hidlHandle(nativeHandle);
- hidl_handle copiedHandle(hidlHandle);
-
- return hidl_memory(name, std::move(copiedHandle), size);
-#else // __ANDROID__
- LOG(FATAL) << "nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const "
- "nn::Memory::HardwareBuffer& memory): Not Available on Host Build";
- (void)memory;
- return (NN_ERROR() << "createHidlMemoryFrom failed").operator nn::GeneralResult<hidl_memory>();
-#endif // __ANDROID__
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
- return hidl_memory(memory.name, NN_TRY(createNativeHandleFrom(memory.handle)), memory.size);
-}
-
-nn::GeneralResult<nn::Memory::Unknown::Handle> unknownHandleFromNativeHandle(
- const native_handle_t* handle) {
- if (handle == nullptr) {
- return NN_ERROR() << "unknownHandleFromNativeHandle failed because handle is nullptr";
- }
-
- std::vector<base::unique_fd> fds =
- NN_TRY(nn::dupFds(handle->data + 0, handle->data + handle->numFds));
-
- std::vector<int> ints(handle->data + handle->numFds,
- handle->data + handle->numFds + handle->numInts);
-
- return nn::Memory::Unknown::Handle{.fds = std::move(fds), .ints = std::move(ints)};
-}
-
} // anonymous namespace
nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
@@ -331,147 +231,4 @@
return **maybeRequestInSharedOut;
}
-nn::GeneralResult<std::vector<uint32_t>> countNumberOfConsumers(
- size_t numberOfOperands, const std::vector<nn::Operation>& operations) {
- return nn::countNumberOfConsumers(numberOfOperands, operations);
-}
-
-nn::GeneralResult<hidl_memory> createHidlMemoryFromSharedMemory(const nn::SharedMemory& memory) {
- if (memory == nullptr) {
- return NN_ERROR() << "Memory must be non-empty";
- }
- return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
-}
-
-#ifdef __ANDROID__
-static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
- return (value + multiple - 1) / multiple * multiple;
-}
-#endif // __ANDROID__
-
-nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
- CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
- if (!memory.valid()) {
- return NN_ERROR() << "Unable to convert invalid hidl_memory";
- }
-
- if (memory.name() == "ashmem") {
- if (memory.handle()->numFds != 1) {
- return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
- << memory.handle()->numFds << " numFds, but expected 1";
- }
- if (memory.handle()->numInts != 0) {
- return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
- << memory.handle()->numInts << " numInts, but expected 0";
- }
- auto handle = nn::Memory::Ashmem{
- .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
- .size = static_cast<size_t>(memory.size()),
- };
- return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
- }
-
- if (memory.name() == "mmap_fd") {
- if (memory.handle()->numFds != 1) {
- return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
- << memory.handle()->numFds << " numFds, but expected 1";
- }
- if (memory.handle()->numInts != 3) {
- return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
- << memory.handle()->numInts << " numInts, but expected 3";
- }
-
- const int fd = memory.handle()->data[0];
- const int prot = memory.handle()->data[1];
- const int lower = memory.handle()->data[2];
- const int higher = memory.handle()->data[3];
- const size_t offset = nn::getOffsetFromInts(lower, higher);
-
- return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
- }
-
- if (memory.name() != "hardware_buffer_blob") {
- auto handle = nn::Memory::Unknown{
- .handle = NN_TRY(unknownHandleFromNativeHandle(memory.handle())),
- .size = static_cast<size_t>(memory.size()),
- .name = memory.name(),
- };
- return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
- }
-
-#ifdef __ANDROID__
- const auto size = memory.size();
- const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
- const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
- const uint32_t width = size;
- const uint32_t height = 1; // height is always 1 for BLOB mode AHardwareBuffer.
- const uint32_t layers = 1; // layers is always 1 for BLOB mode AHardwareBuffer.
-
- // AHardwareBuffer_createFromHandle() might fail because an allocator
- // expects a specific stride value. In that case, we try to guess it by
- // aligning the width to small powers of 2.
- // TODO(b/174120849): Avoid stride assumptions.
- AHardwareBuffer* hardwareBuffer = nullptr;
- status_t status = UNKNOWN_ERROR;
- for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
- const uint32_t stride = roundUpToMultiple(width, alignment);
- AHardwareBuffer_Desc desc{
- .width = width,
- .height = height,
- .layers = layers,
- .format = format,
- .usage = usage,
- .stride = stride,
- };
- status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
- AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
- &hardwareBuffer);
- if (status == NO_ERROR) {
- break;
- }
- }
- if (status != NO_ERROR) {
- return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
- << "Can't create AHardwareBuffer from handle. Error: " << status;
- }
-
- return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
-#else // __ANDROID__
- LOG(FATAL) << "nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const "
- "hidl_memory& memory): Not Available on Host Build";
- return (NN_ERROR() << "createSharedMemoryFromHidlMemory failed")
- .
- operator nn::GeneralResult<nn::SharedMemory>();
-#endif // __ANDROID__
-}
-
-nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle) {
- base::unique_fd fd = NN_TRY(nn::dupFd(handle.get()));
- return createNativeHandleFrom(std::move(fd), {});
-}
-
-nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
- if (handle == nullptr) {
- return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle is nullptr";
- }
- if (handle->numFds != 1 || handle->numInts != 0) {
- return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle does not only "
- "hold a single fd";
- }
- return nn::dupFd(handle->data[0]);
-}
-
-nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
- const std::vector<nn::SyncFence>& syncFences) {
- hidl_vec<hidl_handle> handles(syncFences.size());
- for (size_t i = 0; i < syncFences.size(); ++i) {
- const auto& handle = syncFences[i].getSharedHandle();
- if (handle == nullptr) {
- return NN_ERROR() << "convertSyncFences failed because sync fence is empty";
- }
- handles[i] = NN_TRY(hidlHandleFromSharedHandle(*handle));
- }
- return handles;
-}
-
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
index fbb8679..c3272ae 100644
--- a/neuralnetworks/utils/service/Android.bp
+++ b/neuralnetworks/utils/service/Android.bp
@@ -25,7 +25,10 @@
cc_library_static {
name: "neuralnetworks_utils_hal_service",
- defaults: ["neuralnetworks_utils_defaults"],
+ defaults: [
+ "neuralnetworks_use_latest_utils_hal_aidl",
+ "neuralnetworks_utils_defaults",
+ ],
srcs: ["src/*"],
local_include_dirs: ["include/nnapi/hal"],
export_include_dirs: ["include"],
@@ -35,15 +38,12 @@
"neuralnetworks_utils_hal_1_1",
"neuralnetworks_utils_hal_1_2",
"neuralnetworks_utils_hal_1_3",
- "neuralnetworks_utils_hal_aidl",
"neuralnetworks_utils_hal_common",
],
shared_libs: [
- "android.hardware.neuralnetworks-V2-ndk",
"android.hardware.neuralnetworks@1.0",
"android.hardware.neuralnetworks@1.1",
"android.hardware.neuralnetworks@1.2",
"android.hardware.neuralnetworks@1.3",
- "libbinder_ndk",
],
}
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.0/vts/OWNERS
similarity index 100%
copy from power/1.0/vts/functional/OWNERS
copy to power/1.0/vts/OWNERS
diff --git a/power/1.1/vts/OWNERS b/power/1.1/vts/OWNERS
new file mode 100644
index 0000000..3a64da7
--- /dev/null
+++ b/power/1.1/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+wvw@google.com
diff --git a/power/1.0/vts/functional/OWNERS b/power/1.1/vts/functional/OWNERS
similarity index 100%
rename from power/1.0/vts/functional/OWNERS
rename to power/1.1/vts/functional/OWNERS
diff --git a/power/1.2/vts/OWNERS b/power/1.2/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.2/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS
diff --git a/power/1.3/vts/OWNERS b/power/1.3/vts/OWNERS
new file mode 100644
index 0000000..4d8c7e9
--- /dev/null
+++ b/power/1.3/vts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 158088
+include ../../1.1/vts/OWNERS
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
index d3f0cf9..44f9865 100644
--- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -606,7 +606,8 @@
if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
/* Wait some time for setting sim power down and then verify it */
updateSimCardStatus();
- EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+ // We cannot assert the consistency of CardState here due to b/203031664
+ // EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
// applications should be an empty vector of AppStatus
EXPECT_EQ(0, cardStatus.applications.size());
}
diff --git a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
index 71b1765..d135a69 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.network/current/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -43,7 +43,7 @@
oneway void imsNetworkStateChanged(in android.hardware.radio.RadioIndicationType type);
oneway void networkScanResult(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.NetworkScanResult result);
oneway void networkStateChanged(in android.hardware.radio.RadioIndicationType type);
- oneway void nitzTimeReceived(in android.hardware.radio.RadioIndicationType type, in String nitzTime, in long receivedTime);
+ oneway void nitzTimeReceived(in android.hardware.radio.RadioIndicationType type, in String nitzTime, in long receivedTimeMs, in long ageMs);
oneway void registrationFailed(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.CellIdentity cellIdentity, in String chosenPlmn, in android.hardware.radio.network.Domain domain, in int causeCode, in int additionalCauseCode);
oneway void restrictedStateChanged(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.PhoneRestrictedState state);
oneway void suppSvcNotify(in android.hardware.radio.RadioIndicationType type, in android.hardware.radio.network.SuppSvcNotification suppSvc);
diff --git a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
index a2fac20..ba7610d 100644
--- a/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
+++ b/radio/aidl/android/hardware/radio/network/IRadioNetworkIndication.aidl
@@ -129,9 +129,15 @@
*
* @param type Type of radio indication
* @param nitzTime NITZ time string in the form "yy/mm/dd,hh:mm:ss(+/-)tz,dt"
- * @param receivedTime milliseconds since boot that the NITZ time was received
+ * @param receivedTimeMs time (in milliseconds since boot) at which RIL sent the NITZ time to
+ * the framework
+ * @param ageMs time in milliseconds indicating how long NITZ was cached in RIL and modem.
+ * This must track true age and therefore must be calculated using clocks that
+ * include the time spend in sleep / low power states. If it can not be guaranteed,
+ * there must not be any caching done at the modem and should fill in 0 for ageMs
*/
- void nitzTimeReceived(in RadioIndicationType type, in String nitzTime, in long receivedTime);
+ void nitzTimeReceived(in RadioIndicationType type, in String nitzTime,
+ in long receivedTimeMs, in long ageMs);
/**
* Report that Registration or a Location/Routing/Tracking Area update has failed.
diff --git a/security/keymint/aidl/OWNERS b/security/OWNERS
similarity index 100%
rename from security/keymint/aidl/OWNERS
rename to security/OWNERS
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 37acfa9..12ce859 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -1307,7 +1307,8 @@
AuthorizationSet expected_sw_enforced, //
AuthorizationSet expected_hw_enforced, //
SecurityLevel security_level,
- const vector<uint8_t>& attestation_cert) {
+ const vector<uint8_t>& attestation_cert,
+ vector<uint8_t>* unique_id) {
X509_Ptr cert(parse_cert_blob(attestation_cert));
EXPECT_TRUE(!!cert.get());
if (!cert.get()) return false;
@@ -1472,6 +1473,10 @@
expected_hw_enforced.Sort();
EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
+ if (unique_id != nullptr) {
+ *unique_id = att_unique_id;
+ }
+
return true;
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index ec3fcf6..7b3b9d4 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -338,7 +338,8 @@
AuthorizationSet expected_sw_enforced, //
AuthorizationSet expected_hw_enforced, //
SecurityLevel security_level,
- const vector<uint8_t>& attestation_cert);
+ const vector<uint8_t>& attestation_cert,
+ vector<uint8_t>* unique_id = nullptr);
string bin2hex(const vector<uint8_t>& data);
X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index e41a851..670043d 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -1621,6 +1621,94 @@
}
/*
+ * NewKeyGenerationTest.EcdsaAttestationUniqueId
+ *
+ * Verifies that creation of an attested ECDSA key with a UNIQUE_ID included.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) {
+ auto get_unique_id = [this](const std::string& app_id, uint64_t datetime,
+ vector<uint8_t>* unique_id) {
+ auto challenge = "hello";
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+ uint64_t serial_int = 0x1010;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+ const AuthorizationSetBuilder builder =
+ AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)
+ .EcdsaSigningKey(EcCurve::P_256)
+ .Digest(Digest::NONE)
+ .AttestationChallenge(challenge)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_CREATION_DATETIME, datetime)
+ .SetDefaultValidity();
+
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(builder));
+ ASSERT_GT(key_blob_.size(), 0U);
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics_);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics_);
+
+ // Check that the unique ID field in the extension is non-empty.
+ EXPECT_TRUE(verify_attestation_record(challenge, app_id, sw_enforced, hw_enforced,
+ SecLevel(), cert_chain_[0].encodedCertificate,
+ unique_id));
+ EXPECT_GT(unique_id->size(), 0);
+ CheckedDeleteKey();
+ };
+
+ // Generate unique ID
+ auto app_id = "foo";
+ uint64_t cert_date = 1619621648000; // Wed Apr 28 14:54:08 2021 in ms since epoch
+ vector<uint8_t> unique_id;
+ get_unique_id(app_id, cert_date, &unique_id);
+
+ // Generating a new key with the same parameters should give the same unique ID.
+ vector<uint8_t> unique_id2;
+ get_unique_id(app_id, cert_date, &unique_id2);
+ EXPECT_EQ(unique_id, unique_id2);
+
+ // Generating a new key with a slightly different date should give the same unique ID.
+ uint64_t rounded_date = cert_date / 2592000000LLU;
+ uint64_t min_date = rounded_date * 2592000000LLU;
+ uint64_t max_date = ((rounded_date + 1) * 2592000000LLU) - 1;
+
+ vector<uint8_t> unique_id3;
+ get_unique_id(app_id, min_date, &unique_id3);
+ EXPECT_EQ(unique_id, unique_id3);
+
+ vector<uint8_t> unique_id4;
+ get_unique_id(app_id, max_date, &unique_id4);
+ EXPECT_EQ(unique_id, unique_id4);
+
+ // A different attestation application ID should yield a different unique ID.
+ auto app_id2 = "different_foo";
+ vector<uint8_t> unique_id5;
+ get_unique_id(app_id2, cert_date, &unique_id5);
+ EXPECT_NE(unique_id, unique_id5);
+
+ // A radically different date should yield a different unique ID.
+ vector<uint8_t> unique_id6;
+ get_unique_id(app_id, 1611621648000, &unique_id6);
+ EXPECT_NE(unique_id, unique_id6);
+
+ vector<uint8_t> unique_id7;
+ get_unique_id(app_id, max_date + 1, &unique_id7);
+ EXPECT_NE(unique_id, unique_id7);
+
+ vector<uint8_t> unique_id8;
+ get_unique_id(app_id, min_date - 1, &unique_id8);
+ EXPECT_NE(unique_id, unique_id8);
+}
+
+/*
* NewKeyGenerationTest.EcdsaAttestationTagNoApplicationId
*
* Verifies that creation of an attested ECDSA key does not include APPLICATION_ID.
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 38f3586..76fb79b 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -222,7 +222,7 @@
// Generate an ECDSA key that is attested by the generated P256 keypair.
AuthorizationSet keyDesc = AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
- .EcdsaSigningKey(256)
+ .EcdsaSigningKey(EcCurve::P_256)
.AttestationChallenge("foo")
.AttestationApplicationId("bar")
.Digest(Digest::NONE)
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index bdb4cdf..e162934 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -25,6 +25,7 @@
cc_library {
name: "libkeymint_support",
+ vendor_available: true,
cflags: [
"-Wall",
"-Wextra",
@@ -44,6 +45,7 @@
"libbase",
"libcrypto",
"libutils",
+ "libhardware",
],
}
diff --git a/security/keymint/support/OWNERS b/security/keymint/support/OWNERS
deleted file mode 100644
index a93b171..0000000
--- a/security/keymint/support/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-seleneh@google.com
-swillden@google.com
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
index 25eace3..c1b5d48 100644
--- a/security/keymint/support/authorization_set.cpp
+++ b/security/keymint/support/authorization_set.cpp
@@ -161,11 +161,6 @@
return EncryptionKey();
}
-AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
- EcdsaKey(key_size);
- return SigningKey();
-}
-
AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
EcdsaKey(curve);
return SigningKey();
diff --git a/security/keymint/support/include/keymint_support/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
index ca51b08..e41a329 100644
--- a/security/keymint/support/include/keymint_support/authorization_set.h
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -281,7 +281,6 @@
AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent);
AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent);
- AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size);
AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve);
AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size);
AuthorizationSetBuilder& TripleDesEncryptionKey(uint32_t key_size);
diff --git a/security/secureclock/aidl/OWNERS b/security/secureclock/aidl/OWNERS
deleted file mode 100644
index a93b171..0000000
--- a/security/secureclock/aidl/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-seleneh@google.com
-swillden@google.com
diff --git a/security/sharedsecret/aidl/OWNERS b/security/sharedsecret/aidl/OWNERS
deleted file mode 100644
index a93b171..0000000
--- a/security/sharedsecret/aidl/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-jbires@google.com
-jdanis@google.com
-seleneh@google.com
-swillden@google.com
diff --git a/tv/cec/1.0/default/Android.bp b/tv/cec/1.0/default/Android.bp
index b4053df..e4c226d 100644
--- a/tv/cec/1.0/default/Android.bp
+++ b/tv/cec/1.0/default/Android.bp
@@ -15,6 +15,7 @@
srcs: [
"HdmiCec.cpp",
"HdmiCecDefault.cpp",
+ "HdmiCecPort.cpp",
],
shared_libs: [
diff --git a/tv/cec/1.0/default/HdmiCecDefault.cpp b/tv/cec/1.0/default/HdmiCecDefault.cpp
index 299bcf0..26ccb7d 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.cpp
+++ b/tv/cec/1.0/default/HdmiCecDefault.cpp
@@ -16,19 +16,22 @@
#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <cutils/properties.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <linux/cec.h>
#include <linux/ioctl.h>
#include <poll.h>
-#include <pthread.h>
-#include <sys/eventfd.h>
-#include <algorithm>
#include "HdmiCecDefault.h"
+#define PROPERTY_DEVICE_TYPE "ro.hdmi.device_type"
+#define MIN_PORT_ID 0
+#define MAX_PORT_ID 15
+#define INVALID_PHYSICAL_ADDRESS 0xFFFF
+
namespace android {
namespace hardware {
namespace tv {
@@ -36,24 +39,14 @@
namespace V1_0 {
namespace implementation {
-// When set to false, all the CEC commands are discarded. True by default after initialization.
-bool mCecEnabled;
-/*
- * When set to false, HAL does not wake up the system upon receiving <Image View On> or
- * <Text View On>. True by default after initialization.
- */
-bool mWakeupEnabled;
-
-int mCecFd;
-int mExitFd;
-pthread_t mEventThread;
-sp<IHdmiCecCallback> mCallback;
+using android::base::GetUintProperty;
+using std::stoi;
+using std::string;
HdmiCecDefault::HdmiCecDefault() {
- mCecFd = -1;
- mExitFd = -1;
mCecEnabled = false;
mWakeupEnabled = false;
+ mCecControlEnabled = false;
mCallback = nullptr;
}
@@ -68,8 +61,8 @@
return Result::FAILURE_INVALID_ARGS;
}
- struct cec_log_addrs cecLogAddrs;
- int ret = ioctl(mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
+ cec_log_addrs cecLogAddrs;
+ int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
if (ret) {
LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
return Result::FAILURE_BUSY;
@@ -135,27 +128,36 @@
cecLogAddrs.features[logAddrIndex][0] = 0;
cecLogAddrs.features[logAddrIndex][1] = 0;
- ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
- if (ret) {
- LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
- return Result::FAILURE_BUSY;
+ // Return failure only if add logical address fails for all the ports
+ Return<Result> result = Result::FAILURE_BUSY;
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Add logical address failed for port " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ } else {
+ result = Result::SUCCESS;
+ }
}
- return Result::SUCCESS;
+ return result;
}
Return<void> HdmiCecDefault::clearLogicalAddress() {
- struct cec_log_addrs cecLogAddrs;
+ cec_log_addrs cecLogAddrs;
memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
- int ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
- if (ret) {
- LOG(ERROR) << "Clear logical Address failed, Error = " << strerror(errno);
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Clear logical Address failed for port " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ }
}
return Void();
}
Return<void> HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) {
uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
callback(Result::FAILURE_INVALID_STATE, addr);
@@ -170,7 +172,7 @@
return SendMessageResult::FAIL;
}
- struct cec_msg cecMsg;
+ cec_msg cecMsg;
memset(&cecMsg, 0, sizeof(cec_msg));
int initiator = static_cast<cec_logical_address_t>(message.initiator);
@@ -182,27 +184,25 @@
}
cecMsg.len = message.body.size() + 1;
- int ret = ioctl(mCecFd, CEC_TRANSMIT, &cecMsg);
+ // Return failure only if send message fails for all the ports
+ Return<SendMessageResult> result = SendMessageResult::FAIL;
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_TRANSMIT, &cecMsg);
- if (ret) {
- LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
- return SendMessageResult::FAIL;
- }
+ if (ret) {
+ LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
+ continue;
+ }
- if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
- LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
- }
+ if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
+ LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
+ }
- switch (cecMsg.tx_status) {
- case CEC_TX_STATUS_OK:
- return SendMessageResult::SUCCESS;
- case CEC_TX_STATUS_ARB_LOST:
- return SendMessageResult::BUSY;
- case CEC_TX_STATUS_NACK:
- return SendMessageResult::NACK;
- default:
- return SendMessageResult::FAIL;
+ if (result != SendMessageResult::SUCCESS) {
+ result = getSendMessageResult(cecMsg.tx_status);
+ }
}
+ return result;
}
Return<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
@@ -227,19 +227,25 @@
}
Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
- uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
- if (ret) {
- LOG(ERROR) << "Get port info failed, Error = " << strerror(errno);
+ hidl_vec<HdmiPortInfo> portInfos(mHdmiCecPorts.size());
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ uint16_t addr = INVALID_PHYSICAL_ADDRESS;
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ if (ret) {
+ LOG(ERROR) << "Get port info failed for port : " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ }
+ HdmiPortType type = HdmiPortType::INPUT;
+ uint32_t deviceType = GetUintProperty<uint32_t>(PROPERTY_DEVICE_TYPE, CEC_DEVICE_PLAYBACK);
+ if (deviceType != CEC_DEVICE_TV && i == MIN_PORT_ID) {
+ type = HdmiPortType::OUTPUT;
+ }
+ portInfos[i] = {.type = type,
+ .portId = mHdmiCecPorts[i]->mPortId,
+ .cecSupported = true,
+ .arcSupported = false,
+ .physicalAddress = addr};
}
-
- unsigned int type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
- hidl_vec<HdmiPortInfo> portInfos(1);
- portInfos[0] = {.type = (type == CEC_DEVICE_TV ? HdmiPortType::INPUT : HdmiPortType::OUTPUT),
- .portId = 1,
- .cecSupported = true,
- .arcSupported = false,
- .physicalAddress = addr};
callback(portInfos);
return Void();
}
@@ -254,7 +260,9 @@
LOG(DEBUG) << "setOption: WAKEUP: " << value;
mWakeupEnabled = value;
break;
- default:
+ case OptionKey::SYSTEM_CEC_CONTROL:
+ LOG(DEBUG) << "setOption: SYSTEM_CEC_CONTROL: " << value;
+ mCecControlEnabled = value;
break;
}
return Void();
@@ -268,9 +276,9 @@
return Void();
}
-Return<bool> HdmiCecDefault::isConnected(int32_t /*portId*/) {
+Return<bool> HdmiCecDefault::isConnected(int32_t portId) {
uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ int ret = ioctl(mHdmiCecPorts[portId]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
return false;
@@ -281,79 +289,70 @@
return true;
}
-// Initialise the cec file descriptor
+int getPortId(string cecFilename) {
+ int portId = stoi(cecFilename.substr(3));
+ if (portId >= MIN_PORT_ID && portId <= MAX_PORT_ID) {
+ return portId;
+ } else {
+ return -1;
+ }
+}
+
+// Initialise the cec file descriptors
Return<Result> HdmiCecDefault::init() {
- const char* path = "/dev/cec0";
- mCecFd = open(path, O_RDWR);
- if (mCecFd < 0) {
- LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
- return Result::FAILURE_NOT_SUPPORTED;
- }
- mExitFd = eventfd(0, EFD_NONBLOCK);
- if (mExitFd < 0) {
- LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
- release();
- return Result::FAILURE_NOT_SUPPORTED;
+ const char* parentPath = "/dev/";
+ DIR* dir = opendir(parentPath);
+ const char* cecFilename = "cec";
+
+ while (struct dirent* dirEntry = readdir(dir)) {
+ string filename = dirEntry->d_name;
+ if (filename.compare(0, 3, cecFilename, 0, 3) == 0) {
+ int portId = getPortId(filename);
+ if (portId == -1) {
+ continue;
+ }
+ shared_ptr<HdmiCecPort> hdmiCecPort(new HdmiCecPort(portId));
+ string filepath = parentPath + filename;
+ Result result = hdmiCecPort->init(filepath.c_str());
+ if (result != Result::SUCCESS) {
+ continue;
+ }
+ thread eventThread(&HdmiCecDefault::event_thread, this, hdmiCecPort.get());
+ mEventThreads.push_back(std::move(eventThread));
+ mHdmiCecPorts.push_back(std::move(hdmiCecPort));
+ }
}
- // Ensure the CEC device supports required capabilities
- struct cec_caps caps = {};
- int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
- if (ret) {
- LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
- release();
- return Result::FAILURE_NOT_SUPPORTED;
- }
-
- if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
- LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
- release();
- return Result::FAILURE_NOT_SUPPORTED;
- }
-
- uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
- ret = ioctl(mCecFd, CEC_S_MODE, &mode);
- if (ret) {
- LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
- release();
- return Result::FAILURE_NOT_SUPPORTED;
- }
-
- /* thread loop for receiving cec messages and hotplug events*/
- if (pthread_create(&mEventThread, NULL, event_thread, NULL)) {
- LOG(ERROR) << "Can't create event thread: " << strerror(errno);
- release();
+ if (mHdmiCecPorts.empty()) {
return Result::FAILURE_NOT_SUPPORTED;
}
mCecEnabled = true;
mWakeupEnabled = true;
+ mCecControlEnabled = true;
return Result::SUCCESS;
}
Return<void> HdmiCecDefault::release() {
- if (mExitFd > 0) {
- uint64_t tmp = 1;
- write(mExitFd, &tmp, sizeof(tmp));
- pthread_join(mEventThread, NULL);
- }
- if (mExitFd > 0) {
- close(mExitFd);
- }
- if (mCecFd > 0) {
- close(mCecFd);
- }
mCecEnabled = false;
mWakeupEnabled = false;
+ mCecControlEnabled = false;
+ for (thread& eventThread : mEventThreads) {
+ if (eventThread.joinable()) {
+ eventThread.join();
+ }
+ }
setCallback(nullptr);
+ mHdmiCecPorts.clear();
+ mEventThreads.clear();
return Void();
}
-void* HdmiCecDefault::event_thread(void*) {
+void HdmiCecDefault::event_thread(HdmiCecPort* hdmiCecPort) {
struct pollfd ufds[3] = {
- {mCecFd, POLLIN, 0},
- {mCecFd, POLLERR, 0},
- {mExitFd, POLLIN, 0},
+ {hdmiCecPort->mCecFd, POLLIN, 0},
+ {hdmiCecPort->mCecFd, POLLERR, 0},
+ {hdmiCecPort->mExitFd, POLLIN, 0},
};
while (1) {
@@ -372,23 +371,23 @@
}
if (ufds[1].revents == POLLERR) { /* CEC Event */
- struct cec_event ev;
- ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
-
- if (!mCecEnabled) {
- continue;
- }
+ cec_event ev;
+ ret = ioctl(hdmiCecPort->mCecFd, CEC_DQEVENT, &ev);
if (ret) {
LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
continue;
}
+ if (!mCecEnabled) {
+ continue;
+ }
+
if (ev.event == CEC_EVENT_STATE_CHANGE) {
if (mCallback != nullptr) {
HotplugEvent hotplugEvent{
.connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
- .portId = 1};
+ .portId = hdmiCecPort->mPortId};
mCallback->onHotplugEvent(hotplugEvent);
} else {
LOG(ERROR) << "No event callback for hotplug";
@@ -397,12 +396,8 @@
}
if (ufds[0].revents == POLLIN) { /* CEC Driver */
- struct cec_msg msg = {};
- ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
-
- if (!mCecEnabled) {
- continue;
- }
+ cec_msg msg = {};
+ ret = ioctl(hdmiCecPort->mCecFd, CEC_RECEIVE, &msg);
if (ret) {
LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
@@ -414,11 +409,20 @@
continue;
}
+ if (!mCecEnabled) {
+ continue;
+ }
+
if (!mWakeupEnabled && isWakeupMessage(msg)) {
LOG(DEBUG) << "Filter wakeup message";
continue;
}
+ if (!mCecControlEnabled && !isTransferableInSleep(msg)) {
+ LOG(DEBUG) << "Filter message in standby mode";
+ continue;
+ }
+
if (mCallback != nullptr) {
size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
CecMessage cecMessage{
@@ -435,14 +439,13 @@
}
}
}
- return NULL;
}
-int HdmiCecDefault::getOpcode(struct cec_msg message) {
- return (static_cast<uint8_t>(message.msg[1]) & 0xff);
+int HdmiCecDefault::getOpcode(cec_msg message) {
+ return static_cast<uint8_t>(message.msg[1]);
}
-bool HdmiCecDefault::isWakeupMessage(struct cec_msg message) {
+bool HdmiCecDefault::isWakeupMessage(cec_msg message) {
int opcode = getOpcode(message);
switch (opcode) {
case CEC_MESSAGE_TEXT_VIEW_ON:
@@ -453,6 +456,61 @@
}
}
+bool HdmiCecDefault::isTransferableInSleep(cec_msg message) {
+ int opcode = getOpcode(message);
+ switch (opcode) {
+ case CEC_MESSAGE_ABORT:
+ case CEC_MESSAGE_DEVICE_VENDOR_ID:
+ case CEC_MESSAGE_GET_CEC_VERSION:
+ case CEC_MESSAGE_GET_MENU_LANGUAGE:
+ case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
+ case CEC_MESSAGE_GIVE_OSD_NAME:
+ case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
+ case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ case CEC_MESSAGE_REPORT_POWER_STATUS:
+ case CEC_MESSAGE_SET_OSD_NAME:
+ case CEC_MESSAGE_DECK_CONTROL:
+ case CEC_MESSAGE_PLAY:
+ case CEC_MESSAGE_IMAGE_VIEW_ON:
+ case CEC_MESSAGE_TEXT_VIEW_ON:
+ case CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+ return true;
+ case CEC_MESSAGE_USER_CONTROL_PRESSED:
+ return isPowerUICommand(message);
+ default:
+ return false;
+ }
+}
+
+int HdmiCecDefault::getFirstParam(cec_msg message) {
+ return static_cast<uint8_t>(message.msg[2]);
+}
+
+bool HdmiCecDefault::isPowerUICommand(cec_msg message) {
+ int uiCommand = getFirstParam(message);
+ switch (uiCommand) {
+ case CEC_OP_UI_CMD_POWER:
+ case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
+ case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Return<SendMessageResult> HdmiCecDefault::getSendMessageResult(int tx_status) {
+ switch (tx_status) {
+ case CEC_TX_STATUS_OK:
+ return SendMessageResult::SUCCESS;
+ case CEC_TX_STATUS_ARB_LOST:
+ return SendMessageResult::BUSY;
+ case CEC_TX_STATUS_NACK:
+ return SendMessageResult::NACK;
+ default:
+ return SendMessageResult::FAIL;
+ }
+}
} // namespace implementation
} // namespace V1_0
} // namespace cec
diff --git a/tv/cec/1.0/default/HdmiCecDefault.h b/tv/cec/1.0/default/HdmiCecDefault.h
index c1bb2c7..6574429 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.h
+++ b/tv/cec/1.0/default/HdmiCecDefault.h
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hardware/hdmi_cec.h>
+#include <linux/cec.h>
+#include <thread>
+#include <vector>
+#include "HdmiCecPort.h"
namespace android {
namespace hardware {
@@ -24,7 +26,12 @@
namespace V1_0 {
namespace implementation {
-struct HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+using std::shared_ptr;
+using std::thread;
+using std::vector;
+
+class HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+ public:
HdmiCecDefault();
~HdmiCecDefault();
// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
@@ -47,11 +54,35 @@
Return<Result> init();
Return<void> release();
- static void* event_thread(void*);
- static int getOpcode(struct cec_msg message);
- static bool isWakeupMessage(struct cec_msg message);
-};
+ private:
+ void event_thread(HdmiCecPort* hdmiCecPort);
+ static int getOpcode(cec_msg message);
+ static int getFirstParam(cec_msg message);
+ static bool isWakeupMessage(cec_msg message);
+ static bool isTransferableInSleep(cec_msg message);
+ static bool isPowerUICommand(cec_msg message);
+ static Return<SendMessageResult> getSendMessageResult(int tx_status);
+
+ vector<thread> mEventThreads;
+ vector<shared_ptr<HdmiCecPort>> mHdmiCecPorts;
+
+ // When set to false, all the CEC commands are discarded. True by default after initialization.
+ bool mCecEnabled;
+ /*
+ * When set to false, HAL does not wake up the system upon receiving <Image View On> or
+ * <Text View On>. True by default after initialization.
+ */
+ bool mWakeupEnabled;
+ /*
+ * Updated when system goes into or comes out of standby mode.
+ * When set to true, Android system is handling CEC commands.
+ * When set to false, microprocessor is handling CEC commands.
+ * True by default after initialization.
+ */
+ bool mCecControlEnabled;
+ sp<IHdmiCecCallback> mCallback;
+};
} // namespace implementation
} // namespace V1_0
} // namespace cec
diff --git a/tv/cec/1.0/default/HdmiCecPort.cpp b/tv/cec/1.0/default/HdmiCecPort.cpp
new file mode 100755
index 0000000..73dda12
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecPort.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
+
+#include <android-base/logging.h>
+#include <errno.h>
+#include <linux/cec.h>
+#include <linux/ioctl.h>
+#include <sys/eventfd.h>
+#include <algorithm>
+
+#include "HdmiCecPort.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+HdmiCecPort::HdmiCecPort(unsigned int portId) {
+ mPortId = portId;
+ mCecFd = -1;
+ mExitFd = -1;
+}
+
+HdmiCecPort::~HdmiCecPort() {
+ release();
+}
+
+// Initialise the cec file descriptor
+Return<Result> HdmiCecPort::init(const char* path) {
+ mCecFd = open(path, O_RDWR);
+ if (mCecFd < 0) {
+ LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+ mExitFd = eventfd(0, EFD_NONBLOCK);
+ if (mExitFd < 0) {
+ LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ // Ensure the CEC device supports required capabilities
+ struct cec_caps caps = {};
+ int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
+ if (ret) {
+ LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
+ LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+ ret = ioctl(mCecFd, CEC_S_MODE, &mode);
+ if (ret) {
+ LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+ return Result::SUCCESS;
+}
+
+Return<void> HdmiCecPort::release() {
+ if (mExitFd > 0) {
+ uint64_t tmp = 1;
+ write(mExitFd, &tmp, sizeof(tmp));
+ }
+ if (mExitFd > 0) {
+ close(mExitFd);
+ }
+ if (mCecFd > 0) {
+ close(mCecFd);
+ }
+ return Void();
+}
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/cec/1.0/default/HdmiCecPort.h b/tv/cec/1.0/default/HdmiCecPort.h
new file mode 100755
index 0000000..2a2fdef
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecPort.h
@@ -0,0 +1,42 @@
+/*
+ * 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/hardware/tv/cec/1.0/IHdmiCec.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+class HdmiCecPort {
+ public:
+ HdmiCecPort(unsigned int portId);
+ ~HdmiCecPort();
+ Return<Result> init(const char* path);
+ Return<void> release();
+
+ unsigned int mPortId;
+ int mCecFd;
+ int mExitFd;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android