Merge changes from topic "11be-basic-support-hal"
* changes:
Wifi: Add implementation for vendor HAL for 11be support
Wifi: Update wifi vendor hal API with changes for 11be support
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
deleted file mode 100644
index 58daca6..0000000
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-
-package vhal_proto;
-
-// CMD messages are from workstation --> VHAL
-// RESP messages are from VHAL --> workstation
-enum MsgType {
- GET_CONFIG_CMD = 0;
- GET_CONFIG_RESP = 1;
- GET_CONFIG_ALL_CMD = 2;
- GET_CONFIG_ALL_RESP = 3;
- GET_PROPERTY_CMD = 4;
- GET_PROPERTY_RESP = 5;
- GET_PROPERTY_ALL_CMD = 6;
- GET_PROPERTY_ALL_RESP = 7;
- SET_PROPERTY_CMD = 8;
- SET_PROPERTY_RESP = 9;
- SET_PROPERTY_ASYNC = 10;
- DEBUG_CMD = 11;
- DEBUG_RESP = 12;
-}
-enum Status {
- RESULT_OK = 0;
- ERROR_UNKNOWN = 1;
- ERROR_UNIMPLEMENTED_CMD = 2;
- ERROR_INVALID_PROPERTY = 3;
- ERROR_INVALID_AREA_ID = 4;
- ERROR_PROPERTY_UNINITIALIZED = 5;
- ERROR_WRITE_ONLY_PROPERTY = 6;
- ERROR_MEMORY_ALLOC_FAILED = 7;
- ERROR_INVALID_OPERATION = 8;
-}
-
-enum VehiclePropStatus {
- AVAILABLE = 0;
- UNAVAILABLE = 1;
- ERROR = 2;
-}
-
-message VehicleAreaConfig {
- required int32 area_id = 1;
- optional sint32 min_int32_value = 2;
- optional sint32 max_int32_value = 3;
- optional sint64 min_int64_value = 4;
- optional sint64 max_int64_value = 5;
- optional float min_float_value = 6;
- optional float max_float_value = 7;
-}
-
-message VehiclePropConfig {
- required int32 prop = 1;
- optional int32 access = 2;
- optional int32 change_mode = 3;
- optional int32 value_type = 4;
- optional int32 supported_areas = 5; // Deprecated - DO NOT USE
- repeated VehicleAreaConfig area_configs = 6;
- optional int32 config_flags = 7;
- repeated int32 config_array = 8;
- optional string config_string = 9;
- optional float min_sample_rate = 10;
- optional float max_sample_rate = 11;
-};
-
-message VehiclePropValue {
- // common data
- required int32 prop = 1;
- optional int32 value_type = 2;
- optional int64 timestamp = 3; // required for valid data from HAL, skipped for set
- optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set
-
- // values
- optional int32 area_id = 4;
- repeated sint32 int32_values = 5; // this also covers boolean value.
- repeated sint64 int64_values = 6;
- repeated float float_values = 7;
- optional string string_value = 8;
- optional bytes bytes_value = 9;
-};
-
-// This structure is used to notify what values to get from the Vehicle HAL
-message VehiclePropGet {
- required int32 prop = 1;
- optional int32 area_id = 2;
-};
-
-message EmulatorMessage {
- required MsgType msg_type = 1;
- optional Status status = 2; // Only for RESP messages
- repeated VehiclePropGet prop = 3; // Provided for getConfig, getProperty commands
- repeated VehiclePropConfig config = 4;
- repeated VehiclePropValue value = 5;
- repeated string debug_commands = 6; // Required for debug command
- optional string debug_result = 7; // Required for debug RESP messages
-};
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
index cab184b..578d045 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h
@@ -23,7 +23,9 @@
#include <IVehicleHardware.h>
#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
+#include <android-base/parseint.h>
#include <android-base/result.h>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <map>
@@ -37,7 +39,7 @@
namespace vehicle {
namespace fake {
-class FakeVehicleHardware final : public IVehicleHardware {
+class FakeVehicleHardware : public IVehicleHardware {
public:
FakeVehicleHardware();
@@ -78,13 +80,21 @@
void registerOnPropertySetErrorEvent(
std::unique_ptr<const PropertySetErrorCallback> callback) override;
+ protected:
+ // mValuePool is also used in mServerSidePropStore.
+ const std::shared_ptr<VehiclePropValuePool> mValuePool;
+ const std::shared_ptr<VehiclePropertyStore> mServerSidePropStore;
+
+ ::android::base::Result<VehiclePropValuePool::RecyclableType> getValue(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
+
+ ::android::base::Result<void> setValue(
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
+
private:
// Expose private methods to unit test.
friend class FakeVehicleHardwareTestHelper;
- // mValuePool is also used in mServerSidePropStore.
- const std::shared_ptr<VehiclePropValuePool> mValuePool;
- const std::shared_ptr<VehiclePropertyStore> mServerSidePropStore;
const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
const std::unique_ptr<FakeUserHal> mFakeUserHal;
std::mutex mCallbackLock;
@@ -120,6 +130,35 @@
::android::base::Result<VehiclePropValuePool::RecyclableType> getUserHalProp(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
bool isHvacPropAndHvacNotAvailable(int32_t propId);
+
+ std::string dumpAllProperties();
+ std::string dumpOnePropertyByConfig(
+ int rowNumber,
+ const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config);
+ std::string dumpOnePropertyById(int32_t propId, int32_t areaId);
+ std::string dumpHelp();
+ std::string dumpListProperties();
+ std::string dumpSpecificProperty(const std::vector<std::string>& options);
+ std::string dumpSetProperties(const std::vector<std::string>& options);
+
+ template <typename T>
+ ::android::base::Result<T> safelyParseInt(int index, const std::string& s) {
+ T out;
+ if (!::android::base::ParseInt(s, &out)) {
+ return ::android::base::Error() << ::android::base::StringPrintf(
+ "non-integer argument at index %d: %s\n", index, s.c_str());
+ }
+ return out;
+ }
+ ::android::base::Result<float> safelyParseFloat(int index, const std::string& s);
+ std::vector<std::string> getOptionValues(const std::vector<std::string>& options,
+ size_t* index);
+ ::android::base::Result<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
+ parseSetPropOptions(const std::vector<std::string>& options);
+ ::android::base::Result<std::vector<uint8_t>> parseHexString(const std::string& s);
+
+ ::android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
+ size_t minSize);
};
} // namespace fake
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
index e75f0e7..097257e 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define LOG_TAG "FakeVehicleHardware"
+#define FAKE_VEHICLEHARDWARE_DEBUG false // STOPSHIP if true.
+
#include "FakeVehicleHardware.h"
#include <DefaultConfig.h>
@@ -22,7 +25,9 @@
#include <PropertyUtils.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
+#include <android-base/parsedouble.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -56,12 +61,31 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
+using ::android::base::EqualsIgnoreCase;
using ::android::base::Error;
+using ::android::base::ParseFloat;
using ::android::base::Result;
+using ::android::base::StartsWith;
+using ::android::base::StringPrintf;
const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/automotive/vhaloverride/";
const char* OVERRIDE_PROPERTY = "persist.vendor.vhal_init_value_override";
+// A list of supported options for "--set" command.
+const std::unordered_set<std::string> SET_PROP_OPTIONS = {
+ // integer.
+ "-i",
+ // 64bit integer.
+ "-i64",
+ // float.
+ "-f",
+ // string.
+ "-s",
+ // bytes in hex format, e.g. 0xDEADBEEF.
+ "-b",
+ // Area id in integer.
+ "-a"};
+
} // namespace
void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDeclaration& config) {
@@ -381,44 +405,26 @@
StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
const std::vector<SetValueRequest>& requests) {
- std::vector<VehiclePropValue> updatedValues;
std::vector<SetValueResult> results;
for (auto& request : requests) {
const VehiclePropValue& value = request.value;
int propId = value.prop;
- ALOGD("Set value for property ID: %d", propId);
+ if (FAKE_VEHICLEHARDWARE_DEBUG) {
+ ALOGD("Set value for property ID: %d", propId);
+ }
SetValueResult setValueResult;
setValueResult.requestId = request.requestId;
- setValueResult.status = StatusCode::OK;
- bool isSpecialValue = false;
- auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue);
-
- if (isSpecialValue) {
- if (!setSpecialValueResult.ok()) {
- ALOGE("failed to set special value for property ID: %d, error: %s, status: %d",
- propId, getErrorMsg(setSpecialValueResult).c_str(),
- getIntErrorCode(setSpecialValueResult));
- setValueResult.status = getErrorCode(setSpecialValueResult);
- }
-
- // Special values are already handled.
- results.push_back(std::move(setValueResult));
- continue;
+ if (auto result = setValue(value); !result.ok()) {
+ ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(),
+ getIntErrorCode(result));
+ setValueResult.status = getErrorCode(result);
+ } else {
+ setValueResult.status = StatusCode::OK;
}
- auto updatedValue = mValuePool->obtain(value);
- int64_t timestamp = elapsedRealtimeNano();
- updatedValue->timestamp = timestamp;
-
- auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
- if (!writeResult.ok()) {
- ALOGE("failed to write value into property store, error: %s, code: %d",
- getErrorMsg(writeResult).c_str(), getIntErrorCode(writeResult));
- setValueResult.status = getErrorCode(writeResult);
- }
results.push_back(std::move(setValueResult));
}
@@ -429,61 +435,378 @@
return StatusCode::OK;
}
+Result<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) {
+ bool isSpecialValue = false;
+ auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue);
+
+ if (isSpecialValue) {
+ if (!setSpecialValueResult.ok()) {
+ return Error(getIntErrorCode(setSpecialValueResult))
+ << StringPrintf("failed to set special value for property ID: %d, error: %s",
+ value.prop, getErrorMsg(setSpecialValueResult).c_str());
+ }
+ return {};
+ }
+
+ auto updatedValue = mValuePool->obtain(value);
+ int64_t timestamp = elapsedRealtimeNano();
+ updatedValue->timestamp = timestamp;
+
+ auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
+ if (!writeResult.ok()) {
+ return Error(getIntErrorCode(writeResult))
+ << StringPrintf("failed to write value into property store, error: %s",
+ getErrorMsg(writeResult).c_str());
+ }
+
+ return {};
+}
+
StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
const std::vector<GetValueRequest>& requests) const {
std::vector<GetValueResult> results;
for (auto& request : requests) {
const VehiclePropValue& value = request.prop;
- ALOGD("getValues(%d)", value.prop);
+
+ if (FAKE_VEHICLEHARDWARE_DEBUG) {
+ ALOGD("getValues(%d)", value.prop);
+ }
GetValueResult getValueResult;
getValueResult.requestId = request.requestId;
- bool isSpecialValue = false;
- auto result = maybeGetSpecialValue(value, &isSpecialValue);
- if (isSpecialValue) {
- if (!result.ok()) {
- ALOGE("failed to get special value: %d, error: %s, code: %d", value.prop,
- getErrorMsg(result).c_str(), getIntErrorCode(result));
- getValueResult.status = getErrorCode(result);
- } else {
- getValueResult.status = StatusCode::OK;
- getValueResult.prop = *result.value();
- }
- results.push_back(std::move(getValueResult));
- continue;
- }
-
- auto readResult = mServerSidePropStore->readValue(value);
- if (!readResult.ok()) {
- StatusCode errorCode = getErrorCode(readResult);
- if (errorCode == StatusCode::NOT_AVAILABLE) {
- ALOGW("%s", "value has not been set yet");
- } else {
- ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(readResult).c_str(),
- toInt(errorCode));
- }
- getValueResult.status = errorCode;
+ auto result = getValue(value);
+ if (!result.ok()) {
+ ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(),
+ getIntErrorCode(result));
+ getValueResult.status = getErrorCode(result);
} else {
getValueResult.status = StatusCode::OK;
- getValueResult.prop = *readResult.value();
+ getValueResult.prop = *result.value();
}
results.push_back(std::move(getValueResult));
}
+ // In a real VHAL implementation, getValue would be async and we would call the callback after
+ // we actually received the values from vehicle bus. Here we are getting the result
+ // synchronously so we could call the callback here.
(*callback)(std::move(results));
return StatusCode::OK;
}
-DumpResult FakeVehicleHardware::dump(const std::vector<std::string>&) {
+Result<VehiclePropValuePool::RecyclableType> FakeVehicleHardware::getValue(
+ const VehiclePropValue& value) const {
+ bool isSpecialValue = false;
+ auto result = maybeGetSpecialValue(value, &isSpecialValue);
+ if (isSpecialValue) {
+ if (!result.ok()) {
+ return Error(getIntErrorCode(result))
+ << StringPrintf("failed to get special value: %d, error: %s", value.prop,
+ getErrorMsg(result).c_str());
+ } else {
+ return std::move(result);
+ }
+ }
+
+ auto readResult = mServerSidePropStore->readValue(value);
+ if (!readResult.ok()) {
+ StatusCode errorCode = getErrorCode(readResult);
+ if (errorCode == StatusCode::NOT_AVAILABLE) {
+ return Error(toInt(errorCode)) << "value has not been set yet";
+ } else {
+ return Error(toInt(errorCode))
+ << "failed to get value, error: " << getErrorMsg(readResult);
+ }
+ }
+
+ return std::move(readResult);
+}
+
+DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) {
DumpResult result;
- // TODO(b/201830716): Implement this.
+ result.callerShouldDumpState = false;
+ if (options.size() == 0) {
+ // We only want caller to dump default state when there is no options.
+ result.callerShouldDumpState = true;
+ result.buffer = dumpAllProperties();
+ return result;
+ }
+ std::string option = options[0];
+ if (EqualsIgnoreCase(option, "--help")) {
+ result.buffer = dumpHelp();
+ return result;
+ } else if (EqualsIgnoreCase(option, "--list")) {
+ result.buffer = dumpListProperties();
+ } else if (EqualsIgnoreCase(option, "--get")) {
+ result.buffer = dumpSpecificProperty(options);
+ } else if (EqualsIgnoreCase(option, "--set")) {
+ result.buffer = dumpSetProperties(options);
+ } else {
+ result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
+ }
return result;
}
+std::string FakeVehicleHardware::dumpHelp() {
+ return "Usage: \n\n"
+ "[no args]: dumps (id and value) all supported properties \n"
+ "--help: shows this help\n"
+ "--list: lists the ids of all supported properties\n"
+ "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n"
+ "--set <PROP> [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] "
+ "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] "
+ "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
+ "Notice that the string, bytes and area value can be set just once, while the other can"
+ " have multiple values (so they're used in the respective array), "
+ "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n";
+}
+
+std::string FakeVehicleHardware::dumpAllProperties() {
+ auto configs = mServerSidePropStore->getAllConfigs();
+ if (configs.size() == 0) {
+ return "no properties to dump\n";
+ }
+ std::string msg = StringPrintf("dumping %zu properties\n", configs.size());
+ int rowNumber = 1;
+ for (const VehiclePropConfig& config : configs) {
+ msg += dumpOnePropertyByConfig(rowNumber++, config);
+ }
+ return msg;
+}
+
+std::string FakeVehicleHardware::dumpOnePropertyByConfig(int rowNumber,
+ const VehiclePropConfig& config) {
+ size_t numberAreas = config.areaConfigs.size();
+ std::string msg = "";
+ if (numberAreas == 0) {
+ msg += StringPrintf("%d: ", rowNumber);
+ msg += dumpOnePropertyById(config.prop, /* areaId= */ 0);
+ return msg;
+ }
+ for (size_t j = 0; j < numberAreas; ++j) {
+ if (numberAreas > 1) {
+ msg += StringPrintf("%d-%zu: ", rowNumber, j);
+ } else {
+ msg += StringPrintf("%d: ", rowNumber);
+ }
+ msg += dumpOnePropertyById(config.prop, config.areaConfigs[j].areaId);
+ }
+ return msg;
+}
+
+std::string FakeVehicleHardware::dumpOnePropertyById(int32_t propId, int32_t areaId) {
+ VehiclePropValue value = {
+ .prop = propId,
+ .areaId = areaId,
+ };
+ bool isSpecialValue = false;
+ auto result = maybeGetSpecialValue(value, &isSpecialValue);
+ if (!isSpecialValue) {
+ result = mServerSidePropStore->readValue(value);
+ }
+ if (!result.ok()) {
+ return StringPrintf("failed to read property value: %d, error: %s, code: %d\n", propId,
+ getErrorMsg(result).c_str(), getIntErrorCode(result));
+
+ } else {
+ return result.value()->toString() + "\n";
+ }
+}
+
+std::string FakeVehicleHardware::dumpListProperties() {
+ auto configs = mServerSidePropStore->getAllConfigs();
+ if (configs.size() == 0) {
+ return "no properties to list\n";
+ }
+ int rowNumber = 1;
+ std::string msg = StringPrintf("listing %zu properties\n", configs.size());
+ for (const auto& config : configs) {
+ msg += StringPrintf("%d: %d\n", rowNumber++, config.prop);
+ }
+ return msg;
+}
+
+Result<void> FakeVehicleHardware::checkArgumentsSize(const std::vector<std::string>& options,
+ size_t minSize) {
+ size_t size = options.size();
+ if (size >= minSize) {
+ return {};
+ }
+ return Error() << StringPrintf("Invalid number of arguments: required at least %zu, got %zu\n",
+ minSize, size);
+}
+
+std::string FakeVehicleHardware::dumpSpecificProperty(const std::vector<std::string>& options) {
+ if (auto result = checkArgumentsSize(options, /*minSize=*/2); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ // options[0] is the command itself...
+ int rowNumber = 1;
+ size_t size = options.size();
+ std::string msg = "";
+ for (size_t i = 1; i < size; ++i) {
+ auto propResult = safelyParseInt<int32_t>(i, options[i]);
+ if (!propResult.ok()) {
+ msg += getErrorMsg(propResult);
+ continue;
+ }
+ int32_t prop = propResult.value();
+ auto result = mServerSidePropStore->getConfig(prop);
+ if (!result.ok()) {
+ msg += StringPrintf("No property %d\n", prop);
+ continue;
+ }
+ msg += dumpOnePropertyByConfig(rowNumber++, *result.value());
+ }
+ return msg;
+}
+
+std::vector<std::string> FakeVehicleHardware::getOptionValues(
+ const std::vector<std::string>& options, size_t* index) {
+ std::vector<std::string> values;
+ while (*index < options.size()) {
+ std::string option = options[*index];
+ if (SET_PROP_OPTIONS.find(option) != SET_PROP_OPTIONS.end()) {
+ return std::move(values);
+ }
+ values.push_back(option);
+ (*index)++;
+ }
+ return std::move(values);
+}
+
+Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions(
+ const std::vector<std::string>& options) {
+ // Options format:
+ // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
+ size_t optionIndex = 1;
+ auto result = safelyParseInt<int32_t>(optionIndex, options[optionIndex]);
+ if (!result.ok()) {
+ return Error() << StringPrintf("Property value: \"%s\" is not a valid int: %s\n",
+ options[optionIndex].c_str(), getErrorMsg(result).c_str());
+ }
+ VehiclePropValue prop = {};
+ prop.prop = result.value();
+ prop.status = VehiclePropertyStatus::AVAILABLE;
+ optionIndex++;
+ std::unordered_set<std::string> parsedOptions;
+
+ while (optionIndex < options.size()) {
+ std::string type = options[optionIndex];
+ optionIndex++;
+ size_t currentIndex = optionIndex;
+ std::vector<std::string> values = getOptionValues(options, &optionIndex);
+ if (parsedOptions.find(type) != parsedOptions.end()) {
+ return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str());
+ }
+ parsedOptions.insert(type);
+ if (EqualsIgnoreCase(type, "-i")) {
+ if (values.size() == 0) {
+ return Error() << "No values specified when using \"-i\"\n";
+ }
+ prop.value.int32Values.resize(values.size());
+ for (size_t i = 0; i < values.size(); i++) {
+ auto int32Result = safelyParseInt<int32_t>(currentIndex + i, values[i]);
+ if (!int32Result.ok()) {
+ return Error()
+ << StringPrintf("Value: \"%s\" is not a valid int: %s\n",
+ values[i].c_str(), getErrorMsg(int32Result).c_str());
+ }
+ prop.value.int32Values[i] = int32Result.value();
+ }
+ } else if (EqualsIgnoreCase(type, "-i64")) {
+ if (values.size() == 0) {
+ return Error() << "No values specified when using \"-i64\"\n";
+ }
+ prop.value.int64Values.resize(values.size());
+ for (size_t i = 0; i < values.size(); i++) {
+ auto int64Result = safelyParseInt<int64_t>(currentIndex + i, values[i]);
+ if (!int64Result.ok()) {
+ return Error()
+ << StringPrintf("Value: \"%s\" is not a valid int64: %s\n",
+ values[i].c_str(), getErrorMsg(int64Result).c_str());
+ }
+ prop.value.int64Values[i] = int64Result.value();
+ }
+ } else if (EqualsIgnoreCase(type, "-f")) {
+ if (values.size() == 0) {
+ return Error() << "No values specified when using \"-f\"\n";
+ }
+ prop.value.floatValues.resize(values.size());
+ for (size_t i = 0; i < values.size(); i++) {
+ auto floatResult = safelyParseFloat(currentIndex + i, values[i]);
+ if (!floatResult.ok()) {
+ return Error()
+ << StringPrintf("Value: \"%s\" is not a valid float: %s\n",
+ values[i].c_str(), getErrorMsg(floatResult).c_str());
+ }
+ prop.value.floatValues[i] = floatResult.value();
+ }
+ } else if (EqualsIgnoreCase(type, "-s")) {
+ if (values.size() != 1) {
+ return Error() << "Expect exact one value when using \"-s\"\n";
+ }
+ prop.value.stringValue = values[0];
+ } else if (EqualsIgnoreCase(type, "-b")) {
+ if (values.size() != 1) {
+ return Error() << "Expect exact one value when using \"-b\"\n";
+ }
+ auto bytesResult = parseHexString(values[0]);
+ if (!bytesResult.ok()) {
+ return Error() << StringPrintf("value: \"%s\" is not a valid hex string: %s\n",
+ values[0].c_str(), getErrorMsg(bytesResult).c_str());
+ }
+ prop.value.byteValues = std::move(bytesResult.value());
+ } else if (EqualsIgnoreCase(type, "-a")) {
+ if (values.size() != 1) {
+ return Error() << "Expect exact one value when using \"-a\"\n";
+ }
+ auto int32Result = safelyParseInt<int32_t>(currentIndex, values[0]);
+ if (!int32Result.ok()) {
+ return Error() << StringPrintf("Area ID: \"%s\" is not a valid int: %s\n",
+ values[0].c_str(), getErrorMsg(int32Result).c_str());
+ }
+ prop.areaId = int32Result.value();
+ } else {
+ return Error() << StringPrintf("Unknown option: %s\n", type.c_str());
+ }
+ }
+
+ return prop;
+}
+
+std::string FakeVehicleHardware::dumpSetProperties(const std::vector<std::string>& options) {
+ if (auto result = checkArgumentsSize(options, 3); !result.ok()) {
+ return getErrorMsg(result);
+ }
+
+ auto parseResult = parseSetPropOptions(options);
+ if (!parseResult.ok()) {
+ return getErrorMsg(parseResult);
+ }
+ VehiclePropValue prop = std::move(parseResult.value());
+ ALOGD("Dump: Setting property: %s", prop.toString().c_str());
+
+ bool isSpecialValue = false;
+ auto setResult = maybeSetSpecialValue(prop, &isSpecialValue);
+
+ if (!isSpecialValue) {
+ auto updatedValue = mValuePool->obtain(prop);
+ updatedValue->timestamp = elapsedRealtimeNano();
+ setResult = mServerSidePropStore->writeValue(std::move(updatedValue));
+ }
+
+ if (setResult.ok()) {
+ return StringPrintf("Set property: %s\n", prop.toString().c_str());
+ }
+ return StringPrintf("failed to set property: %s, error: %s\n", prop.toString().c_str(),
+ getErrorMsg(setResult).c_str());
+}
+
StatusCode FakeVehicleHardware::checkHealth() {
- // TODO(b/201830716): Implement this.
+ // Always return OK for checkHealth.
return StatusCode::OK;
}
@@ -544,6 +867,49 @@
}
}
+Result<float> FakeVehicleHardware::safelyParseFloat(int index, const std::string& s) {
+ float out;
+ if (!ParseFloat(s, &out)) {
+ return Error() << StringPrintf("non-float argument at index %d: %s\n", index, s.c_str());
+ }
+ return out;
+}
+
+Result<std::vector<uint8_t>> FakeVehicleHardware::parseHexString(const std::string& s) {
+ std::vector<uint8_t> bytes;
+ if (s.size() % 2 != 0) {
+ return Error() << StringPrintf("invalid hex string: %s, should have even size\n",
+ s.c_str());
+ }
+ if (!StartsWith(s, "0x")) {
+ return Error() << StringPrintf("hex string should start with \"0x\", got %s\n", s.c_str());
+ }
+ std::string subs = s.substr(2);
+ std::transform(subs.begin(), subs.end(), subs.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+
+ bool highDigit = true;
+ for (size_t i = 0; i < subs.size(); i++) {
+ char c = subs[i];
+ uint8_t v;
+ if (c >= '0' && c <= '9') {
+ v = c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ v = c - 'a' + 10;
+ } else {
+ return Error() << StringPrintf("invalid character %c in hex string %s\n", c,
+ subs.c_str());
+ }
+ if (highDigit) {
+ bytes.push_back(v * 16);
+ } else {
+ bytes[bytes.size() - 1] += v;
+ }
+ highDigit = !highDigit;
+ }
+ return bytes;
+}
+
} // namespace fake
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
index 970d044..0812c2a 100644
--- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
+++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp
@@ -24,6 +24,7 @@
#include <android-base/expected.h>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
@@ -52,13 +53,16 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::android::base::expected;
+using ::android::base::StringPrintf;
using ::android::base::unexpected;
using ::testing::ContainerEq;
+using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::IsSubsetOf;
using ::testing::WhenSortedBy;
constexpr int INVALID_PROP_ID = 0;
+constexpr char CAR_MAKE[] = "Default Car";
} // namespace
@@ -1203,6 +1207,261 @@
}));
}
+TEST_F(FakeVehicleHardwareTest, testDumpAllProperties) {
+ std::vector<std::string> options;
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_TRUE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("dumping .+ properties"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpHelp) {
+ std::vector<std::string> options;
+ options.push_back("--help");
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Usage: "));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpListProperties) {
+ std::vector<std::string> options;
+ options.push_back("--list");
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("listing .+ properties"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificProperties) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+ std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+ std::string prop2 = std::to_string(toInt(VehicleProperty::TIRE_PRESSURE));
+ options.push_back(prop1);
+ options.push_back(prop2);
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer,
+ ContainsRegex(StringPrintf("1:.*prop: %s.*\n2-0:.*prop: %s.*\n2-1:.*prop: %s.*\n",
+ prop1.c_str(), prop2.c_str(), prop2.c_str())));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesInvalidProp) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+ std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
+ std::string prop2 = std::to_string(INVALID_PROP_ID);
+ options.push_back(prop1);
+ options.push_back(prop2);
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex(StringPrintf("1:.*prop: %s.*\nNo property %d\n",
+ prop1.c_str(), INVALID_PROP_ID)));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesNoArg) {
+ std::vector<std::string> options;
+ options.push_back("--get");
+
+ // No arguments.
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Invalid number of arguments"));
+}
+
+TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
+ std::vector<std::string> options;
+ options.push_back("--invalid");
+
+ DumpResult result = getHardware()->dump(options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
+}
+
+struct SetPropTestCase {
+ std::string test_name;
+ std::vector<std::string> options;
+ bool success;
+ std::string errorMsg = "";
+};
+
+class FakeVehicleHardwareSetPropTest : public FakeVehicleHardwareTest,
+ public testing::WithParamInterface<SetPropTestCase> {};
+
+TEST_P(FakeVehicleHardwareSetPropTest, cmdSetOneProperty) {
+ const SetPropTestCase& tc = GetParam();
+
+ DumpResult result = getHardware()->dump(tc.options);
+ ASSERT_FALSE(result.callerShouldDumpState);
+ ASSERT_NE(result.buffer, "");
+ if (tc.success) {
+ ASSERT_THAT(result.buffer, ContainsRegex("Set property:"));
+ } else {
+ ASSERT_THAT(result.buffer, ContainsRegex(tc.errorMsg));
+ }
+}
+
+std::vector<SetPropTestCase> GenSetPropParams() {
+ std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
+ return {
+ {"success_set_string", {"--set", infoMakeProperty, "-s", CAR_MAKE}, true},
+ {"success_set_bytes", {"--set", infoMakeProperty, "-b", "0xdeadbeef"}, true},
+ {"success_set_bytes_caps", {"--set", infoMakeProperty, "-b", "0xDEADBEEF"}, true},
+ {"success_set_int", {"--set", infoMakeProperty, "-i", "2147483647"}, true},
+ {"success_set_ints",
+ {"--set", infoMakeProperty, "-i", "2147483647", "0", "-2147483648"},
+ true},
+ {"success_set_int64",
+ {"--set", infoMakeProperty, "-i64", "-9223372036854775808"},
+ true},
+ {"success_set_int64s",
+ {"--set", infoMakeProperty, "-i64", "-9223372036854775808", "0",
+ "9223372036854775807"},
+ true},
+ {"success_set_float", {"--set", infoMakeProperty, "-f", "1.175494351E-38"}, true},
+ {"success_set_floats",
+ {"--set", infoMakeProperty, "-f", "-3.402823466E+38", "0", "3.402823466E+38"},
+ true},
+ {"success_set_area", {"--set", infoMakeProperty, "-a", "2147483647"}, true},
+ {"fail_no_options", {"--set", infoMakeProperty}, false, "Invalid number of arguments"},
+ {"fail_less_than_4_options",
+ {"--set", infoMakeProperty, "-i"},
+ false,
+ "No values specified"},
+ {"fail_unknown_options", {"--set", infoMakeProperty, "-abcd"}, false, "Unknown option"},
+ {"fail_invalid_property",
+ {"--set", "not valid", "-s", CAR_MAKE},
+ false,
+ "not a valid int"},
+ {"fail_duplicate_string",
+ {"--set", infoMakeProperty, "-s", CAR_MAKE, "-s", CAR_MAKE},
+ false,
+ "Duplicate \"-s\" options"},
+ {"fail_multiple_strings",
+ {"--set", infoMakeProperty, "-s", CAR_MAKE, CAR_MAKE},
+ false,
+ "Expect exact one value"},
+ {"fail_no_string_value",
+ {"--set", infoMakeProperty, "-s", "-a", "1234"},
+ false,
+ "Expect exact one value"},
+ {"fail_duplicate_bytes",
+ {"--set", infoMakeProperty, "-b", "0xdeadbeef", "-b", "0xdeadbeef"},
+ false,
+ "Duplicate \"-b\" options"},
+ {"fail_multiple_bytes",
+ {"--set", infoMakeProperty, "-b", "0xdeadbeef", "0xdeadbeef"},
+ false,
+ "Expect exact one value"},
+ {"fail_invalid_bytes",
+ {"--set", infoMakeProperty, "-b", "0xgood"},
+ false,
+ "not a valid hex string"},
+ {"fail_invalid_bytes_no_prefix",
+ {"--set", infoMakeProperty, "-b", "deadbeef"},
+ false,
+ "not a valid hex string"},
+ {"fail_invalid_int",
+ {"--set", infoMakeProperty, "-i", "abc"},
+ false,
+ "not a valid int"},
+ {"fail_int_out_of_range",
+ {"--set", infoMakeProperty, "-i", "2147483648"},
+ false,
+ "not a valid int"},
+ {"fail_no_int_value",
+ {"--set", infoMakeProperty, "-i", "-s", CAR_MAKE},
+ false,
+ "No values specified"},
+ {"fail_invalid_int64",
+ {"--set", infoMakeProperty, "-i64", "abc"},
+ false,
+ "not a valid int64"},
+ {"fail_int64_out_of_range",
+ {"--set", infoMakeProperty, "-i64", "-9223372036854775809"},
+ false,
+ "not a valid int64"},
+ {"fail_no_int64_value",
+ {"--set", infoMakeProperty, "-i64", "-s", CAR_MAKE},
+ false,
+ "No values specified"},
+ {"fail_invalid_float",
+ {"--set", infoMakeProperty, "-f", "abc"},
+ false,
+ "not a valid float"},
+ {"fail_float_out_of_range",
+ {"--set", infoMakeProperty, "-f", "-3.402823466E+39"},
+ false,
+ "not a valid float"},
+ {"fail_no_float_value",
+ {"--set", infoMakeProperty, "-f", "-s", CAR_MAKE},
+ false,
+ "No values specified"},
+ {"fail_multiple_areas",
+ {"--set", infoMakeProperty, "-a", "2147483648", "0"},
+ false,
+ "Expect exact one value"},
+ {"fail_invalid_area",
+ {"--set", infoMakeProperty, "-a", "abc"},
+ false,
+ "not a valid int"},
+ {"fail_area_out_of_range",
+ {"--set", infoMakeProperty, "-a", "2147483648"},
+ false,
+ "not a valid int"},
+ {"fail_no_area_value",
+ {"--set", infoMakeProperty, "-a", "-s", CAR_MAKE},
+ false,
+ "Expect exact one value"},
+ };
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FakeVehicleHardwareSetPropTests, FakeVehicleHardwareSetPropTest,
+ testing::ValuesIn(GenSetPropParams()),
+ [](const testing::TestParamInfo<FakeVehicleHardwareSetPropTest::ParamType>& info) {
+ return info.param.test_name;
+ });
+
+TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) {
+ std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
+ getHardware()->dump({"--set", infoMakeProperty, "-s", CAR_MAKE,
+ "-b", "0xdeadbeef", "-i", "2147483647",
+ "0", "-2147483648", "-i64", "-9223372036854775808",
+ "0", "9223372036854775807", "-f", "-3.402823466E+38",
+ "0", "3.402823466E+38", "-a", "123"});
+ VehiclePropValue requestProp;
+ requestProp.prop = toInt(VehicleProperty::INFO_MAKE);
+ requestProp.areaId = 123;
+ auto result = getValue(requestProp);
+ ASSERT_TRUE(result.ok());
+ VehiclePropValue value = result.value();
+ ASSERT_EQ(value.prop, toInt(VehicleProperty::INFO_MAKE));
+ ASSERT_EQ(value.areaId, 123);
+ ASSERT_STREQ(CAR_MAKE, value.value.stringValue.c_str());
+ uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef};
+ ASSERT_FALSE(memcmp(bytes, value.value.byteValues.data(), sizeof(bytes)));
+ ASSERT_EQ(3u, value.value.int32Values.size());
+ ASSERT_EQ(2147483647, value.value.int32Values[0]);
+ ASSERT_EQ(0, value.value.int32Values[1]);
+ ASSERT_EQ(-2147483648, value.value.int32Values[2]);
+ ASSERT_EQ(3u, value.value.int64Values.size());
+ // -9223372036854775808 is not a valid literal since '-' and '9223372036854775808' would be two
+ // tokens and the later does not fit in unsigned long long.
+ ASSERT_EQ(-9223372036854775807 - 1, value.value.int64Values[0]);
+ ASSERT_EQ(0, value.value.int64Values[1]);
+ ASSERT_EQ(9223372036854775807, value.value.int64Values[2]);
+ ASSERT_EQ(3u, value.value.floatValues.size());
+ ASSERT_EQ(-3.402823466E+38f, value.value.floatValues[0]);
+ ASSERT_EQ(0.0f, value.value.floatValues[1]);
+ ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]);
+}
+
} // namespace fake
} // namespace vehicle
} // namespace automotive
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
index 833707a..15a6278 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
@@ -79,7 +79,7 @@
GetSetValuesClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
// Sends the results to this client.
- void sendResults(const std::vector<ResultType>& results);
+ void sendResults(std::vector<ResultType>&& results);
// Sends each result separately to this client. Each result would be sent through one callback
// invocation.
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index 62b2627..5e7adfc 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -39,13 +39,6 @@
namespace automotive {
namespace vehicle {
-// private namespace
-namespace defaultvehiclehal_impl {
-
-constexpr int INVALID_MEMORY_FD = -1;
-
-} // namespace defaultvehiclehal_impl
-
class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::vehicle::BnVehicle {
public:
using CallbackType =
@@ -79,6 +72,7 @@
const std::vector<int32_t>& propIds) override;
::ndk::ScopedAStatus returnSharedMemory(const CallbackType& callback,
int64_t sharedMemoryId) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
IVehicleHardware* getHardware();
@@ -215,6 +209,8 @@
void monitorBinderLifeCycle(const CallbackType& callback);
+ bool checkDumpPermission();
+
template <class T>
static std::shared_ptr<T> getOrCreateClient(
std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
index 4b7c2f3..7b2111b 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/ParcelableUtils.h
@@ -29,6 +29,9 @@
namespace automotive {
namespace vehicle {
+// Turns the values into a stable large parcelable that could be sent via binder.
+// If values is small enough, it would be put into output.payloads, otherwise a shared memory file
+// would be created and output.sharedMemoryFd would be filled in.
template <class T1, class T2>
::ndk::ScopedAStatus vectorToStableLargeParcelable(std::vector<T1>&& values, T2* output) {
output->payloads = std::move(values);
@@ -44,6 +47,9 @@
// 'sharedMemoryFd' field.
output->payloads.clear();
output->sharedMemoryFd = std::move(*fd);
+ } else {
+ output->sharedMemoryFd = ::ndk::ScopedFileDescriptor();
+ // Do not modify payloads.
}
return ::ndk::ScopedAStatus::ok();
}
diff --git a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
index 5ccef55..098bfee 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/ConnectedClient.cpp
@@ -84,9 +84,9 @@
// Send all the GetValue/SetValue results through callback in a single callback invocation.
template <class ResultType, class ResultsType>
void sendGetOrSetValueResults(std::shared_ptr<IVehicleCallback> callback,
- const std::vector<ResultType>& results) {
+ std::vector<ResultType>&& results) {
ResultsType parcelableResults;
- ScopedAStatus status = vectorToStableLargeParcelable(results, &parcelableResults);
+ ScopedAStatus status = vectorToStableLargeParcelable(std::move(results), &parcelableResults);
if (status.isOk()) {
if (ScopedAStatus callbackStatus = callCallback(callback, parcelableResults);
!callbackStatus.isOk()) {
@@ -99,7 +99,8 @@
ALOGE("failed to marshal result into large parcelable, error: "
"%s, code: %d",
status.getMessage(), statusCode);
- sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback, results);
+ sendGetOrSetValueResultsSeparately<ResultType, ResultsType>(callback,
+ parcelableResults.payloads);
}
// The timeout callback for GetValues/SetValues.
@@ -115,7 +116,7 @@
.status = StatusCode::TRY_AGAIN,
});
}
- sendGetOrSetValueResults<ResultType, ResultsType>(callback, timeoutResults);
+ sendGetOrSetValueResults<ResultType, ResultsType>(callback, std::move(timeoutResults));
}
// The on-results callback for GetValues/SetValues.
@@ -123,7 +124,7 @@
void getOrSetValuesCallback(
const void* clientId,
std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback,
- std::vector<ResultType> results, std::shared_ptr<PendingRequestPool> requestPool) {
+ std::vector<ResultType>&& results, std::shared_ptr<PendingRequestPool> requestPool) {
std::unordered_set<int64_t> requestIds;
for (const auto& result : results) {
requestIds.insert(result.requestId);
@@ -145,7 +146,7 @@
}
if (!results.empty()) {
- sendGetOrSetValueResults<ResultType, ResultsType>(callback, results);
+ sendGetOrSetValueResults<ResultType, ResultsType>(callback, std::move(results));
}
}
@@ -156,9 +157,9 @@
std::shared_ptr<IVehicleCallback> callback, const SetValueResult& result);
template void sendGetOrSetValueResults<GetValueResult, GetValueResults>(
- std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
+ std::shared_ptr<IVehicleCallback> callback, std::vector<GetValueResult>&& results);
template void sendGetOrSetValueResults<SetValueResult, SetValueResults>(
- std::shared_ptr<IVehicleCallback> callback, const std::vector<SetValueResult>& results);
+ std::shared_ptr<IVehicleCallback> callback, std::vector<SetValueResult>&& results);
template void sendGetOrSetValueResultsSeparately<GetValueResult, GetValueResults>(
std::shared_ptr<IVehicleCallback> callback, const std::vector<GetValueResult>& results);
@@ -175,11 +176,11 @@
template void getOrSetValuesCallback<GetValueResult, GetValueResults>(
const void* clientId,
std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback,
- std::vector<GetValueResult> results, std::shared_ptr<PendingRequestPool> requestPool);
+ std::vector<GetValueResult>&& results, std::shared_ptr<PendingRequestPool> requestPool);
template void getOrSetValuesCallback<SetValueResult, SetValueResults>(
const void* clientId,
std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback> callback,
- std::vector<SetValueResult> results, std::shared_ptr<PendingRequestPool> requestPool);
+ std::vector<SetValueResult>&& results, std::shared_ptr<PendingRequestPool> requestPool);
} // namespace
@@ -230,9 +231,8 @@
}
template <class ResultType, class ResultsType>
-void GetSetValuesClient<ResultType, ResultsType>::sendResults(
- const std::vector<ResultType>& results) {
- return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, results);
+void GetSetValuesClient<ResultType, ResultsType>::sendResults(std::vector<ResultType>&& results) {
+ return sendGetOrSetValueResults<ResultType, ResultsType>(mCallback, std::move(results));
}
template <class ResultType, class ResultsType>
@@ -283,7 +283,8 @@
// TODO(b/205189110): Use memory pool here and fill in sharedMemoryId.
VehiclePropValues vehiclePropValues;
int32_t sharedMemoryFileCount = 0;
- ScopedAStatus status = vectorToStableLargeParcelable(updatedValues, &vehiclePropValues);
+ ScopedAStatus status =
+ vectorToStableLargeParcelable(std::move(updatedValues), &vehiclePropValues);
if (!status.isOk()) {
int statusCode = status.getServiceSpecificError();
ALOGE("subscribe: failed to marshal result into large parcelable, error: "
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index 3e088c5..c0a66da 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -24,6 +24,8 @@
#include <android-base/result.h>
#include <android-base/stringprintf.h>
+#include <android/binder_ibinder.h>
+#include <private/android_filesystem_config.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -387,7 +389,7 @@
if (!failedResults.empty()) {
// First send the failed results we already know back to the client.
- client->sendResults(failedResults);
+ client->sendResults(std::move(failedResults));
}
if (hardwareRequests.empty()) {
@@ -476,7 +478,7 @@
if (!failedResults.empty()) {
// First send the failed results we already know back to the client.
- client->sendResults(failedResults);
+ client->sendResults(std::move(failedResults));
}
if (hardwareRequests.empty()) {
@@ -718,6 +720,39 @@
mLinkToDeathImpl = std::move(impl);
}
+bool DefaultVehicleHal::checkDumpPermission() {
+ uid_t uid = AIBinder_getCallingUid();
+ return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;
+}
+
+binder_status_t DefaultVehicleHal::dump(int fd, const char** args, uint32_t numArgs) {
+ if (!checkDumpPermission()) {
+ dprintf(fd, "Caller must be root, system or shell");
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ std::vector<std::string> options;
+ for (uint32_t i = 0; i < numArgs; i++) {
+ options.push_back(args[i]);
+ }
+ DumpResult result = mVehicleHardware->dump(options);
+ dprintf(fd, "%s", (result.buffer + "\n").c_str());
+ if (!result.callerShouldDumpState) {
+ dprintf(fd, "Skip dumping Vehicle HAL State.\n");
+ return STATUS_OK;
+ }
+ dprintf(fd, "Vehicle HAL State: \n");
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ dprintf(fd, "Containing %zu property configs\n", mConfigsByPropId.size());
+ dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
+ dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
+ dprintf(fd, "Currently have %zu subscription clients\n",
+ mSubscriptionClients->countClients());
+ }
+ return STATUS_OK;
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
index bd4a565..bdb0d31 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/ConnectedClientTest.cpp
@@ -80,7 +80,8 @@
GetValuesClient client(getPool(), getCallbackClient());
- client.sendResults(results);
+ auto resultsCopy = results;
+ client.sendResults(std::move(resultsCopy));
auto maybeGetValueResults = getCallback()->nextGetValueResults();
ASSERT_TRUE(maybeGetValueResults.has_value());
@@ -160,7 +161,8 @@
SetValuesClient client(getPool(), getCallbackClient());
- client.sendResults(results);
+ auto resultsCopy = results;
+ client.sendResults(std::move(resultsCopy));
auto maybeSetValueResults = getCallback()->nextSetValueResults();
ASSERT_TRUE(maybeSetValueResults.has_value());
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index ff355c3..7443d5b 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -27,6 +27,7 @@
#include <android-base/thread_annotations.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <sys/mman.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -75,6 +76,7 @@
using ::ndk::ScopedFileDescriptor;
using ::ndk::SpAIBinder;
+using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
@@ -371,7 +373,7 @@
return mVhal->mOnBinderDiedContexts[clientId].get();
}
- bool countOnBinderDiedContexts() {
+ size_t countOnBinderDiedContexts() {
std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
return mVhal->mOnBinderDiedContexts.size();
}
@@ -444,6 +446,7 @@
if (result.value() != nullptr) {
requests.payloads.clear();
requests.sharedMemoryFd = std::move(*result.value());
+ requests.payloads.clear();
}
return {};
}
@@ -1544,6 +1547,45 @@
<< "expect OnBinderDied context to be deleted when binder is unlinked";
}
+TEST_F(DefaultVehicleHalTest, testDumpCallerShouldDump) {
+ std::string buffer = "Dump from hardware";
+ getHardware()->setDumpResult({
+ .callerShouldDumpState = true,
+ .buffer = buffer,
+ });
+ int fd = memfd_create("memfile", 0);
+ getClient()->dump(fd, nullptr, 0);
+
+ lseek(fd, 0, SEEK_SET);
+ char buf[10240] = {};
+ read(fd, buf, sizeof(buf));
+ close(fd);
+
+ std::string msg(buf);
+
+ ASSERT_THAT(msg, ContainsRegex(buffer + "\nVehicle HAL State: \n"));
+}
+
+TEST_F(DefaultVehicleHalTest, testDumpCallerShouldNotDump) {
+ std::string buffer = "Dump from hardware";
+ getHardware()->setDumpResult({
+ .callerShouldDumpState = false,
+ .buffer = buffer,
+ });
+ int fd = memfd_create("memfile", 0);
+ getClient()->dump(fd, nullptr, 0);
+
+ lseek(fd, 0, SEEK_SET);
+ char buf[10240] = {};
+ read(fd, buf, sizeof(buf));
+ close(fd);
+
+ std::string msg(buf);
+
+ ASSERT_THAT(msg, ContainsRegex(buffer));
+ ASSERT_EQ(msg.find("Vehicle HAL State: "), std::string::npos);
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
index eec32dd..66aef7c 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.cpp
@@ -71,13 +71,15 @@
&mGetValueResponses);
}
+void MockVehicleHardware::setDumpResult(DumpResult result) {
+ mDumpResult = result;
+}
+
DumpResult MockVehicleHardware::dump(const std::vector<std::string>&) {
- // TODO(b/200737967): mock this.
- return DumpResult{};
+ return mDumpResult;
}
StatusCode MockVehicleHardware::checkHealth() {
- // TODO(b/200737967): mock this.
return StatusCode::OK;
}
diff --git a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
index 0844de1..74d4fae 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/vhal/test/MockVehicleHardware.h
@@ -79,6 +79,7 @@
void setStatus(const char* functionName,
::aidl::android::hardware::automotive::vehicle::StatusCode status);
void setSleepTime(int64_t timeInNano);
+ void setDumpResult(DumpResult result);
private:
mutable std::mutex mLock;
@@ -114,6 +115,8 @@
const std::vector<RequestType>& requests,
std::list<std::vector<RequestType>>* storedRequests,
std::list<std::vector<ResultType>>* storedResponses) const REQUIRES(mLock);
+
+ DumpResult mDumpResult;
};
} // namespace vehicle
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/proto/Android.bp
similarity index 94%
rename from automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
rename to automotive/vehicle/proto/Android.bp
index 3307bd6..683f128 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
+++ b/automotive/vehicle/proto/Android.bp
@@ -25,8 +25,8 @@
cc_library_static {
name: "android.hardware.automotive.vehicle@2.0-libproto-native",
visibility: [
- "//hardware/interfaces/automotive/vehicle/2.0/default:__subpackages__",
- "//device/generic/car/emulator/vhal_v2_0:__subpackages__",
+ "//hardware/interfaces/automotive/vehicle:__subpackages__",
+ "//device/generic/car/emulator:__subpackages__",
],
vendor: true,
host_supported: true,
diff --git a/automotive/vehicle/proto/VehicleHalProto.proto b/automotive/vehicle/proto/VehicleHalProto.proto
new file mode 100644
index 0000000..0dafe8c
--- /dev/null
+++ b/automotive/vehicle/proto/VehicleHalProto.proto
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package vhal_proto;
+
+// CMD messages are from workstation --> VHAL
+// RESP messages are from VHAL --> workstation
+enum MsgType {
+ GET_CONFIG_CMD = 0;
+ GET_CONFIG_RESP = 1;
+ GET_CONFIG_ALL_CMD = 2;
+ GET_CONFIG_ALL_RESP = 3;
+ GET_PROPERTY_CMD = 4;
+ GET_PROPERTY_RESP = 5;
+ GET_PROPERTY_ALL_CMD = 6;
+ GET_PROPERTY_ALL_RESP = 7;
+ SET_PROPERTY_CMD = 8;
+ SET_PROPERTY_RESP = 9;
+ SET_PROPERTY_ASYNC = 10;
+ DEBUG_CMD = 11;
+ DEBUG_RESP = 12;
+}
+enum Status {
+ RESULT_OK = 0;
+ ERROR_UNKNOWN = 1;
+ ERROR_UNIMPLEMENTED_CMD = 2;
+ ERROR_INVALID_PROPERTY = 3;
+ ERROR_INVALID_AREA_ID = 4;
+ ERROR_PROPERTY_UNINITIALIZED = 5;
+ ERROR_WRITE_ONLY_PROPERTY = 6;
+ ERROR_MEMORY_ALLOC_FAILED = 7;
+ ERROR_INVALID_OPERATION = 8;
+}
+
+enum VehiclePropStatus {
+ AVAILABLE = 0;
+ UNAVAILABLE = 1;
+ ERROR = 2;
+}
+
+message VehicleAreaConfig {
+ required int32 area_id = 1;
+ optional sint32 min_int32_value = 2;
+ optional sint32 max_int32_value = 3;
+ optional sint64 min_int64_value = 4;
+ optional sint64 max_int64_value = 5;
+ optional float min_float_value = 6;
+ optional float max_float_value = 7;
+}
+
+message VehiclePropConfig {
+ required int32 prop = 1;
+ optional int32 access = 2;
+ optional int32 change_mode = 3;
+ optional int32 value_type = 4;
+ optional int32 supported_areas = 5; // Deprecated - DO NOT USE
+ repeated VehicleAreaConfig area_configs = 6;
+ optional int32 config_flags = 7;
+ repeated int32 config_array = 8;
+ optional string config_string = 9;
+ optional float min_sample_rate = 10;
+ optional float max_sample_rate = 11;
+};
+
+message VehiclePropValue {
+ // common data
+ required int32 prop = 1;
+ optional int32 value_type = 2;
+ optional int64 timestamp = 3; // required for valid data from HAL, skipped for set
+ optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set
+
+ // values
+ optional int32 area_id = 4;
+ repeated sint32 int32_values = 5; // this also covers boolean value.
+ repeated sint64 int64_values = 6;
+ repeated float float_values = 7;
+ optional string string_value = 8;
+ optional bytes bytes_value = 9;
+};
+
+// This structure is used to notify what values to get from the Vehicle HAL
+message VehiclePropGet {
+ required int32 prop = 1;
+ optional int32 area_id = 2;
+};
+
+message EmulatorMessage {
+ required MsgType msg_type = 1;
+ optional Status status = 2; // Only for RESP messages
+ repeated VehiclePropGet prop = 3; // Provided for getConfig, getProperty commands
+ repeated VehiclePropConfig config = 4;
+ repeated VehiclePropValue value = 5;
+ repeated string debug_commands = 6; // Required for debug command
+ optional string debug_result = 7; // Required for debug RESP messages
+};
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
index e2a08a0..948c04a 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecCapabilities.aidl
@@ -37,11 +37,16 @@
android.hardware.bluetooth.audio.CodecType codecType;
android.hardware.bluetooth.audio.CodecCapabilities.Capabilities capabilities;
@VintfStability
+ parcelable VendorCapabilities {
+ ParcelableHolder extension;
+ }
+ @VintfStability
union Capabilities {
android.hardware.bluetooth.audio.SbcCapabilities sbcCapabilities;
android.hardware.bluetooth.audio.AacCapabilities aacCapabilities;
android.hardware.bluetooth.audio.LdacCapabilities ldacCapabilities;
android.hardware.bluetooth.audio.AptxCapabilities aptxCapabilities;
android.hardware.bluetooth.audio.Lc3Capabilities lc3Capabilities;
+ android.hardware.bluetooth.audio.CodecCapabilities.VendorCapabilities vendorCapabilities;
}
}
diff --git a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
index 34ebd60..32bccd8 100644
--- a/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
+++ b/bluetooth/audio/aidl/aidl_api/android.hardware.bluetooth.audio/current/android/hardware/bluetooth/audio/CodecConfiguration.aidl
@@ -40,11 +40,18 @@
boolean isScmstEnabled;
android.hardware.bluetooth.audio.CodecConfiguration.CodecSpecific config;
@VintfStability
+ parcelable VendorConfiguration {
+ int vendorId;
+ char codecId;
+ ParcelableHolder codecConfig;
+ }
+ @VintfStability
union CodecSpecific {
android.hardware.bluetooth.audio.SbcConfiguration sbcConfig;
android.hardware.bluetooth.audio.AacConfiguration aacConfig;
android.hardware.bluetooth.audio.LdacConfiguration ldacConfig;
android.hardware.bluetooth.audio.AptxConfiguration aptxConfig;
android.hardware.bluetooth.audio.Lc3Configuration lc3Config;
+ android.hardware.bluetooth.audio.CodecConfiguration.VendorConfiguration vendorConfig;
}
}
diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
index 5bf0252..41e2431 100644
--- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
+++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecCapabilities.aidl
@@ -30,12 +30,17 @@
@VintfStability
parcelable CodecCapabilities {
@VintfStability
+ parcelable VendorCapabilities {
+ ParcelableHolder extension;
+ }
+ @VintfStability
union Capabilities {
SbcCapabilities sbcCapabilities;
AacCapabilities aacCapabilities;
LdacCapabilities ldacCapabilities;
AptxCapabilities aptxCapabilities;
Lc3Capabilities lc3Capabilities;
+ VendorCapabilities vendorCapabilities;
}
CodecType codecType;
Capabilities capabilities;
diff --git a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
index 9e43f22..3679537 100644
--- a/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
+++ b/bluetooth/audio/aidl/android/hardware/bluetooth/audio/CodecConfiguration.aidl
@@ -30,12 +30,19 @@
@VintfStability
parcelable CodecConfiguration {
@VintfStability
+ parcelable VendorConfiguration {
+ int vendorId;
+ char codecId;
+ ParcelableHolder codecConfig;
+ }
+ @VintfStability
union CodecSpecific {
SbcConfiguration sbcConfig;
AacConfiguration aacConfig;
LdacConfiguration ldacConfig;
AptxConfiguration aptxConfig;
Lc3Configuration lc3Config;
+ VendorConfiguration vendorConfig;
}
CodecType codecType;
/**
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
index 380732f..516ebe8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.cpp
@@ -78,7 +78,7 @@
.bitsPerSample = {24},
};
-static const Lc3Capabilities kDefaultOffloadLc3Capability = {
+static const Lc3Capabilities kDefaultA2dpOffloadLc3Capability = {
.samplingFrequencyHz = {44100, 48000},
.frameDurationUs = {7500, 10000},
.channelMode = {ChannelMode::MONO, ChannelMode::STEREO},
@@ -285,11 +285,11 @@
const Lc3Configuration lc3_data =
codec_specific.get<CodecConfiguration::CodecSpecific::lc3Config>();
- if (ContainedInVector(kDefaultOffloadLc3Capability.samplingFrequencyHz,
+ if (ContainedInVector(kDefaultA2dpOffloadLc3Capability.samplingFrequencyHz,
lc3_data.samplingFrequencyHz) &&
- ContainedInVector(kDefaultOffloadLc3Capability.frameDurationUs,
+ ContainedInVector(kDefaultA2dpOffloadLc3Capability.frameDurationUs,
lc3_data.frameDurationUs) &&
- ContainedInVector(kDefaultOffloadLc3Capability.channelMode,
+ ContainedInVector(kDefaultA2dpOffloadLc3Capability.channelMode,
lc3_data.channelMode)) {
return true;
}
@@ -352,11 +352,10 @@
case CodecType::LC3:
codec_capability.capabilities
.set<CodecCapabilities::Capabilities::lc3Capabilities>(
- kDefaultOffloadLc3Capability);
+ kDefaultA2dpOffloadLc3Capability);
break;
case CodecType::UNKNOWN:
case CodecType::VENDOR:
- codec_capability = {};
break;
}
}
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
index c542ce5..0259a7e 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioCodecs.h
@@ -45,11 +45,7 @@
const SessionType& session_type, const CodecConfiguration& codec_config);
static bool IsOffloadLeAudioConfigurationValid(
- const SessionType& session_type, const Lc3Configuration& codec_config);
-
- static bool IsOffloadLeAudioConfigurationValid(
- const SessionType& session_type,
- const LeAudioConfiguration& codec_config);
+ const SessionType& session_type, const LeAudioConfiguration&);
static std::vector<LeAudioCodecCapabilitiesSetting>
GetLeAudioOffloadCodecCapabilities(const SessionType& session_type);
@@ -77,8 +73,6 @@
const CodecConfiguration::CodecSpecific& codec_specific);
static bool IsOffloadLc3ConfigurationValid(
const CodecConfiguration::CodecSpecific& codec_specific);
- static bool IsOffloadLeAudioConfigurationValid(
- const SessionType& session_type, const LeAudioCodecConfiguration&);
};
} // namespace audio
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
index 8052b01..f626db8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.cpp
@@ -35,22 +35,8 @@
static constexpr int kWritePollMs = 1; // polled non-blocking interval
static constexpr int kReadPollMs = 1; // polled non-blocking interval
-const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {};
-const LeAudioConfiguration kInvalidLeAudioConfiguration = {};
-AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
- {};
-AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
-AudioConfiguration BluetoothAudioSession::invalidLeOffloadAudioConfig = {};
-
BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
- : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {
- invalidSoftwareAudioConfiguration.set<AudioConfiguration::pcmConfig>(
- kInvalidPcmConfiguration);
- invalidOffloadAudioConfiguration.set<AudioConfiguration::a2dpConfig>(
- kInvalidCodecConfiguration);
- invalidLeOffloadAudioConfig.set<AudioConfiguration::leAudioConfig>(
- kInvalidLeAudioConfiguration);
-}
+ : session_type_(session_type), stack_iface_(nullptr), data_mq_(nullptr) {}
/***
*
@@ -72,13 +58,7 @@
} else if (!UpdateDataPath(mq_desc)) {
LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
<< " MqDescriptor Invalid";
- if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
- audio_config_ = std::make_unique<AudioConfiguration>(
- invalidOffloadAudioConfiguration);
- } else {
- audio_config_ = std::make_unique<AudioConfiguration>(
- invalidSoftwareAudioConfiguration);
- }
+ audio_config_ = nullptr;
} else {
stack_iface_ = stack_iface;
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
@@ -91,13 +71,7 @@
std::lock_guard<std::recursive_mutex> guard(mutex_);
bool toggled = IsSessionReady();
LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
- if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
- audio_config_ =
- std::make_unique<AudioConfiguration>(invalidOffloadAudioConfiguration);
- } else {
- audio_config_ =
- std::make_unique<AudioConfiguration>(invalidSoftwareAudioConfiguration);
- }
+ audio_config_ = nullptr;
stack_iface_ = nullptr;
UpdateDataPath(nullptr);
if (toggled) {
@@ -111,17 +85,17 @@
*
***/
-const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+const AudioConfiguration BluetoothAudioSession::GetAudioConfig() {
std::lock_guard<std::recursive_mutex> guard(mutex_);
if (!IsSessionReady()) {
switch (session_type_) {
case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
- return invalidOffloadAudioConfiguration;
+ return AudioConfiguration(CodecConfiguration{});
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
- return invalidLeOffloadAudioConfig;
+ return AudioConfiguration(LeAudioConfiguration{});
default:
- return invalidSoftwareAudioConfiguration;
+ return AudioConfiguration(PcmConfiguration{});
}
}
return *audio_config_;
@@ -164,7 +138,7 @@
session_type_ ==
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH ||
(data_mq_ != nullptr && data_mq_->isValid()));
- return stack_iface_ != nullptr && is_mq_valid;
+ return stack_iface_ != nullptr && is_mq_valid && audio_config_ != nullptr;
}
/***
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
index 85fd571..73bc0f8 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSession.h
@@ -144,7 +144,7 @@
* The control function is for the bluetooth_audio module to get the current
* AudioConfiguration
***/
- const AudioConfiguration& GetAudioConfig();
+ const AudioConfiguration GetAudioConfig();
/***
* The report function is used to report that the Bluetooth stack has notified
@@ -173,14 +173,6 @@
// Return if IBluetoothAudioProviderFactory implementation existed
static bool IsAidlAvailable();
- static constexpr PcmConfiguration kInvalidPcmConfiguration = {};
- // can't be constexpr because of non-literal type
- static const CodecConfiguration kInvalidCodecConfiguration;
-
- static AudioConfiguration invalidSoftwareAudioConfiguration;
- static AudioConfiguration invalidOffloadAudioConfiguration;
- static AudioConfiguration invalidLeOffloadAudioConfig;
-
private:
// using recursive_mutex to allow hwbinder to re-enter again.
std::recursive_mutex mutex_;
diff --git a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
index a3ed428..aff01e5 100644
--- a/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
+++ b/bluetooth/audio/utils/aidl_session/BluetoothAudioSessionControl.h
@@ -79,11 +79,15 @@
BluetoothAudioSessionInstance::GetSessionInstance(session_type);
if (session_ptr != nullptr) {
return session_ptr->GetAudioConfig();
- } else if (session_type ==
- SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
- return BluetoothAudioSession::invalidOffloadAudioConfiguration;
- } else {
- return BluetoothAudioSession::invalidSoftwareAudioConfiguration;
+ }
+ switch (session_type) {
+ case SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+ return AudioConfiguration(CodecConfiguration{});
+ case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+ case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
+ return AudioConfiguration(LeAudioConfiguration{});
+ default:
+ return AudioConfiguration(PcmConfiguration{});
}
}
diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
index 284d0bb..632a389 100644
--- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
+++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware.cpp
@@ -93,16 +93,6 @@
std::unordered_map<uint16_t, std::shared_ptr<PortStatusCallbacks_2_2>>>
legacy_callback_table;
-const static std::unordered_map<SessionType_2_0, SessionType>
- session_type_2_0_to_aidl_map{
- {SessionType_2_0::A2DP_SOFTWARE_ENCODING_DATAPATH,
- SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH},
- {SessionType_2_0::A2DP_HARDWARE_OFFLOAD_DATAPATH,
- SessionType::A2DP_HARDWARE_OFFLOAD_ENCODING_DATAPATH},
- {SessionType_2_0::HEARING_AID_SOFTWARE_ENCODING_DATAPATH,
- SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH},
- };
-
const static std::unordered_map<SessionType_2_1, SessionType>
session_type_2_1_to_aidl_map{
{SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH,
@@ -121,18 +111,6 @@
SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH},
};
-const static std::unordered_map<int32_t, SampleRate_2_0>
- sample_rate_to_hidl_2_0_map{
- {44100, SampleRate_2_0::RATE_44100},
- {48000, SampleRate_2_0::RATE_48000},
- {88200, SampleRate_2_0::RATE_88200},
- {96000, SampleRate_2_0::RATE_96000},
- {176400, SampleRate_2_0::RATE_176400},
- {192000, SampleRate_2_0::RATE_192000},
- {16000, SampleRate_2_0::RATE_16000},
- {24000, SampleRate_2_0::RATE_24000},
- };
-
const static std::unordered_map<int32_t, SampleRate_2_1>
sample_rate_to_hidl_2_1_map{
{44100, SampleRate_2_1::RATE_44100},
@@ -211,20 +189,6 @@
{LdacQualityIndex::ABR, LdacQualityIndex_2_0::QUALITY_ABR},
};
-const static std::unordered_map<LeAudioMode, LeAudioMode_2_2>
- leaudio_mode_to_hidl_map{
- {LeAudioMode::UNKNOWN, LeAudioMode_2_2::UNKNOWN},
- {LeAudioMode::UNICAST, LeAudioMode_2_2::UNICAST},
- {LeAudioMode::BROADCAST, LeAudioMode_2_2::BROADCAST},
- };
-
-inline SessionType from_session_type_2_0(
- const SessionType_2_0& session_type_hidl) {
- auto it = session_type_2_0_to_aidl_map.find(session_type_hidl);
- if (it != session_type_2_0_to_aidl_map.end()) return it->second;
- return SessionType::UNKNOWN;
-}
-
inline SessionType from_session_type_2_1(
const SessionType_2_1& session_type_hidl) {
auto it = session_type_2_1_to_aidl_map.find(session_type_hidl);
@@ -232,6 +196,11 @@
return SessionType::UNKNOWN;
}
+inline SessionType from_session_type_2_0(
+ const SessionType_2_0& session_type_hidl) {
+ return from_session_type_2_1(static_cast<SessionType_2_1>(session_type_hidl));
+}
+
inline HidlStatus to_hidl_status(const BluetoothAudioStatus& status) {
switch (status) {
case BluetoothAudioStatus::SUCCESS:
@@ -243,18 +212,19 @@
}
}
-inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) {
- auto it = sample_rate_to_hidl_2_0_map.find(sample_rate_hz);
- if (it != sample_rate_to_hidl_2_0_map.end()) return it->second;
- return SampleRate_2_0::RATE_UNKNOWN;
-}
-
inline SampleRate_2_1 to_hidl_sample_rate_2_1(const int32_t sample_rate_hz) {
auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
if (it != sample_rate_to_hidl_2_1_map.end()) return it->second;
return SampleRate_2_1::RATE_UNKNOWN;
}
+inline SampleRate_2_0 to_hidl_sample_rate_2_0(const int32_t sample_rate_hz) {
+ auto it = sample_rate_to_hidl_2_1_map.find(sample_rate_hz);
+ if (it != sample_rate_to_hidl_2_1_map.end())
+ return static_cast<SampleRate_2_0>(it->second);
+ return SampleRate_2_0::RATE_UNKNOWN;
+}
+
inline BitsPerSample_2_0 to_hidl_bits_per_sample(const int8_t bit_per_sample) {
switch (bit_per_sample) {
case 16:
@@ -449,18 +419,49 @@
inline Lc3CodecConfig_2_1 to_hidl_leaudio_config_2_1(
const LeAudioConfiguration& leaudio_config) {
- auto& unicast_config =
- leaudio_config.modeConfig
- .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
+ Lc3CodecConfig_2_1 hidl_lc3_codec_config = {
+ .audioChannelAllocation = 0,
+ };
+ if (leaudio_config.modeConfig.getTag() ==
+ LeAudioConfiguration::LeAudioModeConfig::unicastConfig) {
+ auto& unicast_config =
+ leaudio_config.modeConfig
+ .get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
+ if (unicast_config.leAudioCodecConfig.getTag() ==
+ LeAudioCodecConfiguration::lc3Config) {
+ LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
+ }
+ auto& le_codec_config = unicast_config.leAudioCodecConfig
+ .get<LeAudioCodecConfiguration::lc3Config>();
- auto& le_codec_config = unicast_config.leAudioCodecConfig
- .get<LeAudioCodecConfiguration::lc3Config>();
+ hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
- Lc3CodecConfig_2_1 hidl_lc3_codec_config;
- hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
+ for (const auto& map : unicast_config.streamMap) {
+ hidl_lc3_codec_config.audioChannelAllocation |=
+ map.audioChannelAllocation;
+ }
+ } else {
+ // NOTE: Broadcast is not officially supported in HIDL
+ auto& bcast_config =
+ leaudio_config.modeConfig
+ .get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>();
+ if (bcast_config.streamMap.empty()) {
+ return hidl_lc3_codec_config;
+ }
+ if (bcast_config.streamMap[0].leAudioCodecConfig.getTag() !=
+ LeAudioCodecConfiguration::lc3Config) {
+ LOG(FATAL) << __func__ << ": unexpected codec type(vendor?)";
+ }
+ auto& le_codec_config =
+ bcast_config.streamMap[0]
+ .leAudioCodecConfig.get<LeAudioCodecConfiguration::lc3Config>();
+ hidl_lc3_codec_config.lc3Config = to_hidl_lc3_config_2_1(le_codec_config);
- hidl_lc3_codec_config.audioChannelAllocation =
- unicast_config.streamMap.size();
+ for (const auto& map : bcast_config.streamMap) {
+ hidl_lc3_codec_config.audioChannelAllocation |=
+ map.audioChannelAllocation;
+ }
+ }
return hidl_lc3_codec_config;
}
@@ -468,13 +469,10 @@
inline LeAudioConfig_2_2 to_hidl_leaudio_config_2_2(
const LeAudioConfiguration& leaudio_config) {
LeAudioConfig_2_2 hidl_leaudio_config;
- if (leaudio_mode_to_hidl_map.find(leaudio_config.mode) !=
- leaudio_mode_to_hidl_map.end()) {
- hidl_leaudio_config.mode = leaudio_mode_to_hidl_map.at(leaudio_config.mode);
- }
if (leaudio_config.modeConfig.getTag() ==
LeAudioConfiguration::LeAudioModeConfig::unicastConfig) {
+ hidl_leaudio_config.mode = LeAudioMode_2_2::UNICAST;
auto& unicast_config =
leaudio_config.modeConfig
.get<LeAudioConfiguration::LeAudioModeConfig::unicastConfig>();
@@ -497,6 +495,7 @@
}
} else if (leaudio_config.modeConfig.getTag() ==
LeAudioConfiguration::LeAudioModeConfig::broadcastConfig) {
+ hidl_leaudio_config.mode = LeAudioMode_2_2::BROADCAST;
auto bcast_config =
leaudio_config.modeConfig
.get<LeAudioConfiguration::LeAudioModeConfig::broadcastConfig>();
@@ -641,6 +640,12 @@
from_session_type_2_0(session_type), buffer, bytes);
}
+size_t HidlToAidlMiddleware_2_0::InReadPcmData(
+ const SessionType_2_0& session_type, void* buffer, size_t bytes) {
+ return BluetoothAudioSessionControl::InReadPcmData(
+ from_session_type_2_0(session_type), buffer, bytes);
+}
+
bool HidlToAidlMiddleware_2_0::IsAidlAvailable() {
return BluetoothAudioSession::IsAidlAvailable();
}
diff --git a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
index d10ee37..b124d8f 100644
--- a/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
+++ b/bluetooth/audio/utils/aidl_session/HidlToAidlMiddleware_2_0.h
@@ -64,6 +64,9 @@
static size_t OutWritePcmData(const SessionType_2_0& session_type,
const void* buffer, size_t bytes);
+
+ static size_t InReadPcmData(const SessionType_2_0& session_type, void* buffer,
+ size_t bytes);
};
} // namespace audio
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
index 6d5608b..283952e 100644
--- a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
@@ -437,6 +437,9 @@
// The control function reads stream from FMQ
size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
+ if (HidlToAidlMiddleware_2_0::IsAidlAvailable())
+ return HidlToAidlMiddleware_2_0::InReadPcmData(session_type_, buffer,
+ bytes);
if (buffer == nullptr || !bytes) return 0;
size_t totalRead = 0;
int ms_timeout = kFmqReceiveTimeoutMs;
diff --git a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
index c812d54..b63e3bb 100644
--- a/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
+++ b/camera/provider/2.7/default/ExternalCameraProviderImpl_2_7.cpp
@@ -292,8 +292,7 @@
} else {
deviceName = std::string("device@3.4/external/") + cameraId;
}
- if (mCameraStatusMap.find(deviceName) != mCameraStatusMap.end()) {
- mCameraStatusMap.erase(deviceName);
+ if (mCameraStatusMap.erase(deviceName) != 0) {
if (mCallbacks != nullptr) {
mCallbacks->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT);
}
@@ -391,4 +390,4 @@
} // namespace provider
} // namespace camera
} // namespace hardware
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 1731c9c..e6dd1bf 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -486,6 +486,13 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.nfc</name>
+ <interface>
+ <name>INfc</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.oemlock</name>
<version>1.0</version>
@@ -752,6 +759,13 @@
<instance>default</instance>
</interface>
</hal>
+ <hal format="aidl" optional="true">
+ <name>android.hardware.usb</name>
+ <interface>
+ <name>IUsb</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
<hal format="hidl" optional="true">
<name>android.hardware.usb.gadget</name>
<version>1.0-2</version>
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index b071f71..fa294ff 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -161,7 +161,8 @@
return mGralloc->allocate(
width, height, /*layerCount*/ 1,
static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
- static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
+ static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY));
}
struct TestParameters {
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
index 1ed15b8..356cdb0 100644
--- a/neuralnetworks/aidl/vts/functional/Android.bp
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -30,6 +30,7 @@
"neuralnetworks_vts_functional_defaults",
"use_libaidlvintf_gtest_helper_static",
],
+ host_supported: true,
srcs: [
"BasicTests.cpp",
"Callbacks.cpp",
@@ -46,18 +47,11 @@
],
shared_libs: [
"libbinder_ndk",
- "libnativewindow",
- "libvndksupport",
],
static_libs: [
- "android.hidl.allocator@1.0",
- "android.hidl.memory@1.0",
"libaidlcommonsupport",
- "libgmock",
- "libhidlmemory",
"libneuralnetworks_common",
"libneuralnetworks_generated_test_harness",
- "libsync",
],
whole_static_libs: [
"neuralnetworks_generated_AIDL_V3_example",
@@ -73,6 +67,34 @@
],
test_suites: [
"general-tests",
- "vts",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libnativewindow",
+ "libvndksupport",
+ ],
+ static_libs: [
+ "libsync",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_config: "AndroidTestDevice.xml",
+ },
+ host: {
+ shared_libs: [
+ "libtextclassifier_hash",
+ ],
+ static_libs: [
+ "neuralnetworks_canonical_sample_driver",
+ "neuralnetworks_utils_hal_adapter_aidl",
+ ],
+ exclude_static_libs: [
+ "VtsHalHidlTestUtils",
+ "libaidlvintf_gtest_helper",
+ ],
+ test_config: "AndroidTestHost.xml",
+ },
+ },
}
diff --git a/neuralnetworks/aidl/vts/functional/AndroidTest.xml b/neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml
similarity index 100%
rename from neuralnetworks/aidl/vts/functional/AndroidTest.xml
rename to neuralnetworks/aidl/vts/functional/AndroidTestDevice.xml
diff --git a/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml
new file mode 100644
index 0000000..7372a31
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/AndroidTestHost.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs VtsHalNeuralnetworksTargetTest.">
+ <test class="com.android.tradefed.testtype.HostGTest" >
+ <option name="module-name" value="VtsHalNeuralnetworksTargetTest" />
+ <option name="native-test-timeout" value="15m" />
+ </test>
+</configuration>
+
diff --git a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
index 77208aa..7451f7e 100644
--- a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
@@ -23,7 +23,6 @@
#include <fcntl.h>
#include <ftw.h>
#include <gtest/gtest.h>
-#include <hidlmemory/mapping.h>
#include <unistd.h>
#include <cstdio>
@@ -34,7 +33,6 @@
#include "Callbacks.h"
#include "GeneratedTestHarness.h"
-#include "MemoryUtils.h"
#include "TestHarness.h"
#include "Utils.h"
#include "VtsHalNeuralnetworks.h"
@@ -229,7 +227,11 @@
// Create cache directory. The cache directory and a temporary cache file is always created
// to test the behavior of prepareModelFromCache, even when caching is not supported.
+#ifdef __ANDROID__
char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
+#else // __ANDROID__
+ char cacheDirTemp[] = "/tmp/TestCompilationCachingXXXXXX";
+#endif // __ANDROID__
char* cacheDir = mkdtemp(cacheDirTemp);
ASSERT_NE(cacheDir, nullptr);
mCacheDir = cacheDir;
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
index 8c8a87a..40f6cd1 100644
--- a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -20,7 +20,6 @@
#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
-#include <android/sync.h>
#include <gtest/gtest.h>
#include <algorithm>
@@ -30,7 +29,6 @@
#include <numeric>
#include <vector>
-#include <MemoryUtils.h>
#include <android/binder_status.h>
#include <nnapi/Result.h>
#include <nnapi/SharedMemory.h>
@@ -43,6 +41,10 @@
#include "Utils.h"
#include "VtsHalNeuralnetworks.h"
+#ifdef __ANDROID__
+#include <android/sync.h>
+#endif // __ANDROID__
+
namespace aidl::android::hardware::neuralnetworks::vts::functional {
namespace nn = ::android::nn;
@@ -281,10 +283,14 @@
} // namespace
void waitForSyncFence(int syncFd) {
- constexpr int kInfiniteTimeout = -1;
ASSERT_GT(syncFd, 0);
+#ifdef __ANDROID__
+ constexpr int kInfiniteTimeout = -1;
int r = sync_wait(syncFd, kInfiniteTimeout);
ASSERT_GE(r, 0);
+#else // __ANDROID__
+ LOG(FATAL) << "waitForSyncFence not supported on host";
+#endif // __ANDROID__
}
Model createModel(const TestModel& testModel) {
@@ -895,7 +901,11 @@
outputTypesList = {OutputType::FULLY_SPECIFIED};
measureTimingList = {false};
executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+#ifdef __ANDROID__
memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE};
+#else // __ANDROID__
+ memoryTypeList = {MemoryType::DEVICE}; // BLOB_AHWB is not supported on the host.
+#endif // __ANDROID__
} break;
case TestKind::FENCED_COMPUTE: {
outputTypesList = {OutputType::FULLY_SPECIFIED};
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
index 97760ae..f8341b1 100644
--- a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "neuralnetworks_aidl_hal_test"
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
#include <android-base/logging.h>
#include <android/binder_auto_utils.h>
#include <android/binder_interface_utils.h>
@@ -33,7 +34,6 @@
#include "Callbacks.h"
#include "GeneratedTestHarness.h"
-#include "MemoryUtils.h"
#include "Utils.h"
#include "VtsHalNeuralnetworks.h"
@@ -191,7 +191,7 @@
}
// A placeholder invalid IPreparedModel class for MemoryDomainAllocateTest.InvalidPreparedModel
-class InvalidPreparedModel : public BnPreparedModel {
+class InvalidPreparedModel final : public IPreparedModel {
public:
ndk::ScopedAStatus executeSynchronously(const Request&, bool, int64_t, int64_t,
ExecutionResult*) override {
@@ -225,6 +225,16 @@
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
}
+ ndk::ScopedAStatus getInterfaceVersion(int32_t* /*interfaceVersion*/) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+ }
+ ndk::ScopedAStatus getInterfaceHash(std::string* /*interfaceHash*/) {
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+ }
+ ndk::SpAIBinder asBinder() override { return ::ndk::SpAIBinder{}; }
+ bool isRemote() override { return true; }
};
template <typename... Args>
diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp
index efd5bca..1bc76f2 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.cpp
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -21,18 +21,20 @@
#include <aidl/android/hardware/neuralnetworks/OperandType.h>
#include <android-base/logging.h>
#include <android/binder_status.h>
-#include <android/hardware_buffer.h>
#include <sys/mman.h>
#include <iostream>
#include <limits>
#include <numeric>
-#include <MemoryUtils.h>
#include <nnapi/SharedMemory.h>
#include <nnapi/hal/aidl/Conversions.h>
#include <nnapi/hal/aidl/Utils.h>
+#ifdef __ANDROID__
+#include <android/hardware_buffer.h>
+#endif // __ANDROID__
+
namespace aidl::android::hardware::neuralnetworks {
using test_helper::TestBuffer;
@@ -140,7 +142,8 @@
return ahwb->mIsValid ? std::move(ahwb) : nullptr;
}
-void TestBlobAHWB::initialize(uint32_t size) {
+void TestBlobAHWB::initialize([[maybe_unused]] uint32_t size) {
+#ifdef __ANDROID__
mIsValid = false;
ASSERT_GT(size, 0);
const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
@@ -164,6 +167,9 @@
mAidlMemory = utils::convert(mMemory).value();
mIsValid = true;
+#else // __ANDROID__
+ LOG(FATAL) << "TestBlobAHWB::initialize not supported on host";
+#endif // __ANDROID__
}
std::string gtestCompliantName(std::string name) {
diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h
index 0db3f8c..4e0a4aa 100644
--- a/neuralnetworks/aidl/vts/functional/Utils.h
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -18,7 +18,6 @@
#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H
#include <android-base/logging.h>
-#include <android/hardware_buffer.h>
#include <gtest/gtest.h>
#include <algorithm>
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
index ad93e6d..51b4805 100644
--- a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
#include "VtsHalNeuralnetworks.h"
#include <android-base/logging.h>
@@ -28,13 +29,19 @@
#include <utility>
#include <TestHarness.h>
-#include <aidl/Vintf.h>
#include <nnapi/hal/aidl/Conversions.h>
#include "Callbacks.h"
#include "GeneratedTestHarness.h"
#include "Utils.h"
+#ifdef __ANDROID__
+#include <aidl/Vintf.h>
+#else // __ANDROID__
+#include <CanonicalDevice.h>
+#include <nnapi/hal/aidl/Adapter.h>
+#endif // __ANDROID__
+
namespace aidl::android::hardware::neuralnetworks::vts::functional {
using implementation::PreparedModelCallback;
@@ -111,6 +118,7 @@
ASSERT_TRUE(deviceIsResponsive);
}
+#ifdef __ANDROID__
static NamedDevice makeNamedDevice(const std::string& name) {
ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
return {name, IDevice::fromBinder(binder)};
@@ -127,6 +135,14 @@
std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
return namedDevices;
}
+#else // __ANDROID__
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+ const std::string name = "nnapi-sample";
+ auto device = std::make_shared<const ::android::nn::sample::Device>(name);
+ auto aidlDevice = adapter::adapt(device);
+ return {{name, aidlDevice}};
+}
+#endif // __ANDROID__
const std::vector<NamedDevice>& getNamedDevices() {
const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
diff --git a/nfc/OWNERS b/nfc/OWNERS
new file mode 100644
index 0000000..7867204
--- /dev/null
+++ b/nfc/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 48448
+alisher@google.com
+georgekgchang@google.com
+jackcwyu@google.com
+
+
diff --git a/nfc/aidl/Android.bp b/nfc/aidl/Android.bp
new file mode 100644
index 0000000..d390c7e
--- /dev/null
+++ b/nfc/aidl/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+ name: "android.hardware.nfc",
+ vendor_available: true,
+ srcs: ["android/hardware/nfc/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ enabled: false,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl
new file mode 100644
index 0000000..7a0ae54
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfc.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.nfc;
+@VintfStability
+interface INfc {
+ void open(in android.hardware.nfc.INfcClientCallback clientCallback);
+ void close(in android.hardware.nfc.NfcCloseType type);
+ void coreInitialized();
+ void factoryReset();
+ android.hardware.nfc.NfcConfig getConfig();
+ void powerCycle();
+ void preDiscover();
+ int write(in byte[] data);
+ void setEnableVerboseLogging(in boolean enable);
+ boolean isVerboseLoggingEnabled();
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl
new file mode 100644
index 0000000..8150e81
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/INfcClientCallback.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.nfc;
+@VintfStability
+interface INfcClientCallback {
+ void sendData(in byte[] data);
+ void sendEvent(in android.hardware.nfc.NfcEvent event, in android.hardware.nfc.NfcStatus status);
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl
new file mode 100644
index 0000000..7d44d48
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcCloseType.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.nfc;
+@Backing(type="int") @VintfStability
+enum NfcCloseType {
+ DISABLE = 0,
+ HOST_SWITCHED_OFF = 1,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl
new file mode 100644
index 0000000..92e0a9a
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcConfig.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.nfc;
+@VintfStability
+parcelable NfcConfig {
+ boolean nfaPollBailOutMode;
+ android.hardware.nfc.PresenceCheckAlgorithm presenceCheckAlgorithm;
+ android.hardware.nfc.ProtocolDiscoveryConfig nfaProprietaryCfg;
+ byte defaultOffHostRoute;
+ byte defaultOffHostRouteFelica;
+ byte defaultSystemCodeRoute;
+ byte defaultSystemCodePowerState;
+ byte defaultRoute;
+ byte offHostESEPipeId;
+ byte offHostSIMPipeId;
+ int maxIsoDepTransceiveLength;
+ byte[] hostAllowlist;
+ byte[] offHostRouteUicc;
+ byte[] offHostRouteEse;
+ byte defaultIsoDepRoute;
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl
new file mode 100644
index 0000000..dda258e
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcEvent.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.nfc;
+@Backing(type="int") @VintfStability
+enum NfcEvent {
+ OPEN_CPLT = 0,
+ CLOSE_CPLT = 1,
+ POST_INIT_CPLT = 2,
+ PRE_DISCOVER_CPLT = 3,
+ HCI_NETWORK_RESET = 4,
+ ERROR = 5,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.aidl
new file mode 100644
index 0000000..2632480
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/NfcStatus.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.nfc;
+@Backing(type="int") @VintfStability
+enum NfcStatus {
+ OK = 0,
+ FAILED = 1,
+ ERR_TRANSPORT = 2,
+ ERR_CMD_TIMEOUT = 3,
+ REFUSED = 4,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl
new file mode 100644
index 0000000..9a9be21
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/PresenceCheckAlgorithm.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.nfc;
+@Backing(type="byte") @VintfStability
+enum PresenceCheckAlgorithm {
+ DEFAULT = 0,
+ I_BLOCK = 1,
+ ISO_DEP_NAK = 2,
+}
diff --git a/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
new file mode 100644
index 0000000..021dfe2
--- /dev/null
+++ b/nfc/aidl/aidl_api/android.hardware.nfc/current/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.nfc;
+@VintfStability
+parcelable ProtocolDiscoveryConfig {
+ byte protocol18092Active;
+ byte protocolBPrime;
+ byte protocolDual;
+ byte protocol15693;
+ byte protocolKovio;
+ byte protocolMifare;
+ byte discoveryPollKovio;
+ byte discoveryPollBPrime;
+ byte discoveryListenBPrime;
+}
diff --git a/nfc/aidl/android/hardware/nfc/INfc.aidl b/nfc/aidl/android/hardware/nfc/INfc.aidl
new file mode 100644
index 0000000..662f8d4
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/INfc.aidl
@@ -0,0 +1,143 @@
+/*
+ * 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.nfc;
+
+import android.hardware.nfc.INfcClientCallback;
+import android.hardware.nfc.NfcCloseType;
+import android.hardware.nfc.NfcConfig;
+import android.hardware.nfc.NfcStatus;
+
+@VintfStability
+interface INfc {
+ /**
+ * Opens the NFC controller device and performs initialization.
+ * This may include patch download and other vendor-specific initialization.
+ *
+ * If open completes successfully, the controller must be ready to perform NCI
+ * initialization - ie accept CORE_RESET and subsequent commands through the write()
+ * call.
+ *
+ * Returns ok() if initialization starts successfully.
+ * If open() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT
+ * callback before continuing.
+ * If a NfcEvent.OPEN_CPLT callback received with status NfcStatus::OK, the controller
+ * must be ready to perform NCI initialization - ie accept CORE_RESET and subsequent
+ * commands through the write() call.
+ */
+ void open(in INfcClientCallback clientCallback);
+
+ /**
+ * Close the NFC HAL and setup NFC controller close type.
+ * Associated resources such as clientCallback must be released.
+ * The clientCallback reference from open() must be invalid after close().
+ * If close() returns ok(), the NCI stack must wait for a NfcEvent.CLOSE_CPLT
+ * callback before continuing.
+ * Returns an error if close may be called more than once.
+ * Calls to any other method which expect a callback after this must return
+ * a service-specific error NfcStatus::FAILED.
+ * HAL close automatically if the client drops the reference to the HAL or
+ * crashes.
+ */
+ void close(in NfcCloseType type);
+
+ /**
+ * coreInitialized() is called after the CORE_INIT_RSP is received from the
+ * NFCC. At this time, the HAL can do any chip-specific configuration.
+ *
+ * If coreInitialized() returns ok(), the NCI stack must wait for a
+ * NfcEvent.POST_INIT_CPLT before continuing.
+ * If coreInitialized() returns an error, the NCI stack must continue immediately.
+ *
+ * coreInitialized() must be called after open() registers the clientCallback
+ * or return a service-specific error NfcStatus::FAILED directly.
+ *
+ */
+ void coreInitialized();
+
+ /**
+ * Clears the NFC chip.
+ *
+ * Must be called during factory reset and/or before the first time the HAL is
+ * initialized after a factory reset.
+ */
+ void factoryReset();
+
+ /**
+ * Fetches vendor specific configurations.
+ * @return NfcConfig indicates support for certain features and
+ * populates the vendor specific configs.
+ */
+ NfcConfig getConfig();
+
+ /**
+ * Restart controller by power cyle;
+ * It's similar to open but just reset the controller without initialize all the
+ * resources.
+ *
+ * If powerCycle() returns ok(), the NCI stack must wait for a NfcEvent.OPEN_CPLT
+ * before continuing.
+ *
+ * powerCycle() must be called after open() registers the clientCallback
+ * or return a service-specific error NfcStatus::FAILED directly.
+ */
+ void powerCycle();
+
+ /**
+ * preDiscover is called every time before starting RF discovery.
+ * It is a good place to do vendor-specific configuration that must be
+ * performed every time RF discovery is about to be started.
+ *
+ * If preDiscover() returns ok(), the NCI stack must wait for a
+ * NfcEvent.PREDISCOVER_CPLT before continuing.
+ *
+ * preDiscover() must be called after open() registers the clientCallback
+ * or return a service-specific error NfcStatus::FAILED directly.
+ *
+ * If preDiscover() reports an error, the NCI stack must start RF discovery immediately.
+ */
+ void preDiscover();
+
+ /**
+ * Performs an NCI write.
+ *
+ * This method may queue writes and return immediately. The only
+ * requirement is that the writes are executed in order.
+ *
+ * @param data
+ * Data packet to transmit NCI Commands and Data Messages over write.
+ * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification.
+ * https://nfc-forum.org/our-work/specification-releases/
+ *
+ * @return number of bytes written to the NFCC.
+ */
+ int write(in byte[] data);
+
+ /**
+ * Set the logging flag for NFC HAL to enable it's verbose logging.
+ * If verbose logging is not supported, the call must not have any effect on logging verbosity.
+ * However, isVerboseLoggingEnabled() must still return the value set by the last call to
+ * setEnableVerboseLogging().
+ * @param enable for setting the verbose logging flag to HAL
+ */
+ void setEnableVerboseLogging(in boolean enable);
+
+ /**
+ * Get the verbose logging flag value from NFC HAL.
+ * @return true if verbose logging flag value is enabled, false if disabled.
+ */
+ boolean isVerboseLoggingEnabled();
+}
diff --git a/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl b/nfc/aidl/android/hardware/nfc/INfcClientCallback.aidl
new file mode 100644
index 0000000..43d4f5c
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/INfcClientCallback.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.
+ */
+
+package android.hardware.nfc;
+
+import android.hardware.nfc.NfcEvent;
+import android.hardware.nfc.NfcStatus;
+
+@VintfStability
+interface INfcClientCallback {
+ /**
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass incomming response data to the stack.
+ *
+ * @param data
+ * Data packet to transmit NCI Responses, Notifications, and Data
+ * Messages over sendData.
+ * Detailed format is defined in NFC Controller Interface (NCI) Technical Specification.
+ * https://nfc-forum.org/our-work/specification-releases/
+ */
+ void sendData(in byte[] data);
+
+ /**
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass events back to the stack.
+ */
+ void sendEvent(in NfcEvent event, in NfcStatus status);
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl b/nfc/aidl/android/hardware/nfc/NfcCloseType.aidl
new file mode 100644
index 0000000..5160532
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcCloseType.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.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Different closing types for NFC HAL.
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcCloseType {
+ /**
+ * Close the NFC controller and free NFC HAL resources.
+ */
+ DISABLE = 0,
+
+ /**
+ * Switch the NFC controller operation mode and free NFC HAL resources.
+ * Enable NFC functionality for off host card emulation usecases in
+ * device(host) power off(switched off) state, if the device
+ * supports power off use cases. If the device doesn't support power
+ * off use cases, this call should be same as DISABLE.
+ */
+ HOST_SWITCHED_OFF = 1,
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcConfig.aidl b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl
new file mode 100644
index 0000000..1b4fcfb
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcConfig.aidl
@@ -0,0 +1,89 @@
+/*
+ * 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.nfc;
+
+import android.hardware.nfc.PresenceCheckAlgorithm;
+import android.hardware.nfc.ProtocolDiscoveryConfig;
+
+/**
+ * Define Nfc related configurations based on:
+ * NFC Controller Interface (NCI) Technical Specification
+ * https://nfc-forum.org/our-work/specification-releases/
+ */
+@VintfStability
+parcelable NfcConfig {
+ /**
+ * If true, NFCC is using bail out mode for either Type A or Type B poll
+ * based on: Nfc-Forum Activity Technical Specification
+ * https://nfc-forum.org/our-work/specification-releases/
+ */
+ boolean nfaPollBailOutMode;
+ PresenceCheckAlgorithm presenceCheckAlgorithm;
+ ProtocolDiscoveryConfig nfaProprietaryCfg;
+ /**
+ * Default off-host route. 0x00 if there aren't any. Refer to NCI spec.
+ */
+ byte defaultOffHostRoute;
+ /**
+ * Default off-host route for Felica. 0x00 if there aren't any. Refer to
+ * NCI spec.
+ */
+ byte defaultOffHostRouteFelica;
+ /**
+ * Default system code route. 0x00 if there aren't any. Refer NCI spec.
+ */
+ byte defaultSystemCodeRoute;
+ /**
+ * Default power state for system code route. 0x00 if there aren't any.
+ * Refer to NCI spec.
+ */
+ byte defaultSystemCodePowerState;
+ /**
+ * Default route for all remaining protocols and technology which haven't
+ * been configured.
+ * Device Host(0x00) is the default. Refer to NCI spec.
+ *
+ */
+ byte defaultRoute;
+ /**
+ * Pipe ID for eSE. 0x00 if there aren't any.
+ */
+ byte offHostESEPipeId;
+ /**
+ * Pipe ID for UICC. 0x00 if there aren't any.
+ */
+ byte offHostSIMPipeId;
+ /**
+ * Extended APDU length for ISO_DEP. If not supported default length is 261
+ */
+ int maxIsoDepTransceiveLength;
+ /**
+ * list of allowed host ids, as per ETSI TS 102 622
+ * https://www.etsi.org/
+ */
+ byte[] hostAllowlist;
+ /**
+ * NFCEE ID for offhost UICC & eSE secure element.
+ * 0x00 if there aren't any. Refer to NCI spec.
+ */
+ byte[] offHostRouteUicc;
+ byte[] offHostRouteEse;
+ /**
+ * Default IsoDep route. 0x00 if there aren't any. Refer to NCI spec.
+ */
+ byte defaultIsoDepRoute;
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcEvent.aidl b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl
new file mode 100644
index 0000000..a78b1cd
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcEvent.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.nfc;
+
+/**
+ * Nfc event to notify nfc status change.
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcEvent {
+ /**
+ * Open complete event to notify upper layer when NFC HAL and NFCC
+ * initialization complete.
+ */
+ OPEN_CPLT = 0,
+ /**
+ * Close complete event to notify upper layer when HAL close is done.
+ */
+ CLOSE_CPLT = 1,
+ /**
+ * Post init complete event to notify upper layer when post init operations
+ * are done.
+ */
+ POST_INIT_CPLT = 2,
+ /**
+ * Pre-discover complete event to notify upper layer when pre-discover
+ * operations are done.
+ */
+ PRE_DISCOVER_CPLT = 3,
+ /**
+ * HCI network reset event to notify upplayer when HCI network needs to
+ * be re-initialized in case of an error.
+ */
+ HCI_NETWORK_RESET = 4,
+ /**
+ * Error event to notify upper layer when there's an unknown error.
+ */
+ ERROR = 5,
+}
diff --git a/nfc/aidl/android/hardware/nfc/NfcStatus.aidl b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl
new file mode 100644
index 0000000..a38d370
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/NfcStatus.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.nfc;
+
+/**
+ * Used to specify the status of the NfcEvent
+ */
+@VintfStability
+@Backing(type="int")
+enum NfcStatus {
+ /**
+ * Default status when NfcEvent with status OK.
+ */
+ OK = 0,
+ /**
+ * Generic error.
+ */
+ FAILED = 1,
+ /**
+ * Transport error.
+ */
+ ERR_TRANSPORT = 2,
+ /**
+ * Command timeout error.
+ */
+ ERR_CMD_TIMEOUT = 3,
+ /**
+ * Refused error when command is rejected.
+ */
+ REFUSED = 4,
+}
diff --git a/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl
new file mode 100644
index 0000000..20e7bff
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/PresenceCheckAlgorithm.aidl
@@ -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.
+ */
+
+package android.hardware.nfc;
+
+/*
+ * Presence Check Algorithm as defined in ISO/IEC 14443-4:
+ * https://www.iso.org/standard/73599.html
+ */
+@VintfStability
+@Backing(type="byte")
+enum PresenceCheckAlgorithm {
+ /**
+ * Let the stack select an algorithm
+ */
+ DEFAULT = 0,
+ /**
+ * ISO-DEP protocol's empty I-block
+ */
+ I_BLOCK = 1,
+ /**
+ * Type - 4 tag protocol iso-dep nak presence check command is sent waiting for
+ * response and notification.
+ */
+ ISO_DEP_NAK = 2,
+}
diff --git a/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.aidl
new file mode 100644
index 0000000..f8e3228
--- /dev/null
+++ b/nfc/aidl/android/hardware/nfc/ProtocolDiscoveryConfig.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.nfc;
+
+/**
+ * Vendor Specific Proprietary Protocol & Discovery Configuration.
+ * Set to 0xFF if not supported.
+ * discovery* fields map to "RF Technology and Mode" in NCI Spec
+ * protocol* fields map to "RF protocols" in NCI Spec
+ */
+@VintfStability
+parcelable ProtocolDiscoveryConfig {
+ byte protocol18092Active;
+ byte protocolBPrime;
+ byte protocolDual;
+ byte protocol15693;
+ byte protocolKovio;
+ byte protocolMifare;
+ byte discoveryPollKovio;
+ byte discoveryPollBPrime;
+ byte discoveryListenBPrime;
+}
diff --git a/nfc/aidl/default/Android.bp b/nfc/aidl/default/Android.bp
new file mode 100644
index 0000000..907d23d
--- /dev/null
+++ b/nfc/aidl/default/Android.bp
@@ -0,0 +1,23 @@
+cc_binary {
+ name: "android.hardware.nfc-service.example",
+ relative_install_path: "hw",
+ init_rc: ["nfc-service-example.rc"],
+ vintf_fragments: ["nfc-service-example.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libutils",
+ "libbinder_ndk",
+ "android.hardware.nfc-V1-ndk",
+ ],
+ srcs: [
+ "main.cpp",
+ "Nfc.cpp",
+ "Vendor_hal_api.cpp",
+ ],
+}
diff --git a/nfc/aidl/default/Nfc.cpp b/nfc/aidl/default/Nfc.cpp
new file mode 100644
index 0000000..a31ac5e
--- /dev/null
+++ b/nfc/aidl/default/Nfc.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "Nfc.h"
+
+#include <android-base/logging.h>
+
+#include "Vendor_hal_api.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace nfc {
+
+std::shared_ptr<INfcClientCallback> Nfc::mCallback = nullptr;
+AIBinder_DeathRecipient* clientDeathRecipient = nullptr;
+
+void OnDeath(void* cookie) {
+ if (Nfc::mCallback != nullptr && !AIBinder_isAlive(Nfc::mCallback->asBinder().get())) {
+ LOG(INFO) << __func__ << " Nfc service has died";
+ Nfc* nfc = static_cast<Nfc*>(cookie);
+ nfc->close(NfcCloseType::DISABLE);
+ }
+}
+
+::ndk::ScopedAStatus Nfc::open(const std::shared_ptr<INfcClientCallback>& clientCallback) {
+ LOG(INFO) << "open";
+ if (clientCallback == nullptr) {
+ LOG(INFO) << "Nfc::open null callback";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ } else {
+ Nfc::mCallback = clientCallback;
+
+ clientDeathRecipient = AIBinder_DeathRecipient_new(OnDeath);
+ auto linkRet = AIBinder_linkToDeath(clientCallback->asBinder().get(), clientDeathRecipient,
+ this /* cookie */);
+ if (linkRet != STATUS_OK) {
+ LOG(ERROR) << __func__ << ": linkToDeath failed: " << linkRet;
+ // Just ignore the error.
+ }
+
+ int ret = Vendor_hal_open(eventCallback, dataCallback);
+ return ret == 0 ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ return ndk::ScopedAStatus::ok();
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::close(NfcCloseType type) {
+ LOG(INFO) << "close";
+ if (Nfc::mCallback == nullptr) {
+ LOG(ERROR) << __func__ << "mCallback null";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ }
+ int ret = 0;
+ if (type == NfcCloseType::HOST_SWITCHED_OFF) {
+ ret = Vendor_hal_close_off();
+ } else {
+ ret = Vendor_hal_close();
+ }
+ Nfc::mCallback = nullptr;
+ AIBinder_DeathRecipient_delete(clientDeathRecipient);
+ clientDeathRecipient = nullptr;
+ return ret == 0 ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+}
+
+::ndk::ScopedAStatus Nfc::coreInitialized() {
+ LOG(INFO) << "coreInitialized";
+ if (Nfc::mCallback == nullptr) {
+ LOG(ERROR) << __func__ << "mCallback null";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ }
+ int ret = Vendor_hal_core_initialized();
+
+ return ret == 0 ? ndk::ScopedAStatus::ok()
+ : ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+}
+
+::ndk::ScopedAStatus Nfc::factoryReset() {
+ LOG(INFO) << "factoryReset";
+ Vendor_hal_factoryReset();
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::getConfig(NfcConfig* _aidl_return) {
+ LOG(INFO) << "getConfig";
+ NfcConfig nfcVendorConfig;
+ Vendor_hal_getConfig(nfcVendorConfig);
+
+ *_aidl_return = nfcVendorConfig;
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::powerCycle() {
+ LOG(INFO) << "powerCycle";
+ if (Nfc::mCallback == nullptr) {
+ LOG(ERROR) << __func__ << "mCallback null";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ }
+ return Vendor_hal_power_cycle() ? ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED))
+ : ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::preDiscover() {
+ LOG(INFO) << "preDiscover";
+ if (Nfc::mCallback == nullptr) {
+ LOG(ERROR) << __func__ << "mCallback null";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ }
+ return Vendor_hal_pre_discover() ? ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED))
+ : ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::write(const std::vector<uint8_t>& data, int32_t* _aidl_return) {
+ LOG(INFO) << "write";
+ if (Nfc::mCallback == nullptr) {
+ LOG(ERROR) << __func__ << "mCallback null";
+ return ndk::ScopedAStatus::fromServiceSpecificError(
+ static_cast<int32_t>(NfcStatus::FAILED));
+ }
+ *_aidl_return = Vendor_hal_write(data.size(), &data[0]);
+ return ndk::ScopedAStatus::ok();
+}
+::ndk::ScopedAStatus Nfc::setEnableVerboseLogging(bool enable) {
+ LOG(INFO) << "setVerboseLogging";
+ Vendor_hal_setVerboseLogging(enable);
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Nfc::isVerboseLoggingEnabled(bool* _aidl_return) {
+ *_aidl_return = Vendor_hal_getVerboseLogging();
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace nfc
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/nfc/aidl/default/Nfc.h b/nfc/aidl/default/Nfc.h
new file mode 100644
index 0000000..1b14534
--- /dev/null
+++ b/nfc/aidl/default/Nfc.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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/nfc/BnNfc.h>
+#include <aidl/android/hardware/nfc/INfcClientCallback.h>
+#include <android-base/logging.h>
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace nfc {
+
+using ::aidl::android::hardware::nfc::NfcCloseType;
+using ::aidl::android::hardware::nfc::NfcConfig;
+using ::aidl::android::hardware::nfc::NfcStatus;
+
+// Default implementation that reports no support NFC.
+struct Nfc : public BnNfc {
+ public:
+ Nfc() = default;
+
+ ::ndk::ScopedAStatus open(const std::shared_ptr<INfcClientCallback>& clientCallback) override;
+ ::ndk::ScopedAStatus close(NfcCloseType type) override;
+ ::ndk::ScopedAStatus coreInitialized() override;
+ ::ndk::ScopedAStatus factoryReset() override;
+ ::ndk::ScopedAStatus getConfig(NfcConfig* _aidl_return) override;
+ ::ndk::ScopedAStatus powerCycle() override;
+ ::ndk::ScopedAStatus preDiscover() override;
+ ::ndk::ScopedAStatus write(const std::vector<uint8_t>& data, int32_t* _aidl_return) override;
+ ::ndk::ScopedAStatus setEnableVerboseLogging(bool enable) override;
+ ::ndk::ScopedAStatus isVerboseLoggingEnabled(bool* _aidl_return) override;
+
+ static void eventCallback(uint8_t event, uint8_t status) {
+ if (mCallback != nullptr) {
+ auto ret = mCallback->sendEvent((NfcEvent)event, (NfcStatus)status);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Failed to send event!";
+ }
+ }
+ }
+
+ static void dataCallback(uint16_t data_len, uint8_t* p_data) {
+ std::vector<uint8_t> data(p_data, p_data + data_len);
+ if (mCallback != nullptr) {
+ auto ret = mCallback->sendData(data);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "Failed to send data!";
+ }
+ }
+ }
+
+ static std::shared_ptr<INfcClientCallback> mCallback;
+};
+
+} // namespace nfc
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/nfc/aidl/default/Vendor_hal_api.cpp b/nfc/aidl/default/Vendor_hal_api.cpp
new file mode 100644
index 0000000..66a2ebc
--- /dev/null
+++ b/nfc/aidl/default/Vendor_hal_api.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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/properties.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+
+#include "Vendor_hal_api.h"
+
+bool logging = false;
+
+int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback) {
+ (void)p_cback;
+ (void)p_data_cback;
+ // nothing to open in this example
+ return -1;
+}
+
+int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data) {
+ (void)data_len;
+ (void)p_data;
+ return -1;
+}
+
+int Vendor_hal_core_initialized() {
+ return -1;
+}
+
+int Vendor_hal_pre_discover() {
+ return -1;
+}
+
+int Vendor_hal_close() {
+ return -1;
+}
+
+int Vendor_hal_close_off() {
+ return -1;
+}
+
+int Vendor_hal_power_cycle() {
+ return -1;
+}
+
+void Vendor_hal_factoryReset() {}
+
+void Vendor_hal_getConfig(NfcConfig& config) {
+ (void)config;
+}
+
+void Vendor_hal_setVerboseLogging(bool enable) {
+ logging = enable;
+}
+
+bool Vendor_hal_getVerboseLogging() {
+ return logging;
+}
diff --git a/nfc/aidl/default/Vendor_hal_api.h b/nfc/aidl/default/Vendor_hal_api.h
new file mode 100644
index 0000000..595c2dd
--- /dev/null
+++ b/nfc/aidl/default/Vendor_hal_api.h
@@ -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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/nfc/INfc.h>
+#include <aidl/android/hardware/nfc/NfcConfig.h>
+#include <aidl/android/hardware/nfc/NfcEvent.h>
+#include <aidl/android/hardware/nfc/NfcStatus.h>
+#include <aidl/android/hardware/nfc/PresenceCheckAlgorithm.h>
+#include <aidl/android/hardware/nfc/ProtocolDiscoveryConfig.h>
+#include "hardware_nfc.h"
+
+using aidl::android::hardware::nfc::NfcConfig;
+using aidl::android::hardware::nfc::NfcEvent;
+using aidl::android::hardware::nfc::NfcStatus;
+using aidl::android::hardware::nfc::PresenceCheckAlgorithm;
+using aidl::android::hardware::nfc::ProtocolDiscoveryConfig;
+
+int Vendor_hal_open(nfc_stack_callback_t* p_cback, nfc_stack_data_callback_t* p_data_cback);
+int Vendor_hal_write(uint16_t data_len, const uint8_t* p_data);
+
+int Vendor_hal_core_initialized();
+
+int Vendor_hal_pre_discover();
+
+int Vendor_hal_close();
+
+int Vendor_hal_close_off();
+
+int Vendor_hal_power_cycle();
+
+void Vendor_hal_factoryReset();
+
+void Vendor_hal_getConfig(NfcConfig& config);
+
+void Vendor_hal_setVerboseLogging(bool enable);
+
+bool Vendor_hal_getVerboseLogging();
diff --git a/nfc/aidl/default/hardware_nfc.h b/nfc/aidl/default/hardware_nfc.h
new file mode 100644
index 0000000..0f856c5
--- /dev/null
+++ b/nfc/aidl/default/hardware_nfc.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
+
+typedef uint8_t nfc_event_t;
+typedef uint8_t nfc_status_t;
+
+/*
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass events back to the stack.
+ */
+typedef void(nfc_stack_callback_t)(nfc_event_t event, nfc_status_t event_status);
+
+/*
+ * The callback passed in from the NFC stack that the HAL
+ * can use to pass incomming data to the stack.
+ */
+typedef void(nfc_stack_data_callback_t)(uint16_t data_len, uint8_t* p_data);
diff --git a/nfc/aidl/default/main.cpp b/nfc/aidl/default/main.cpp
new file mode 100644
index 0000000..0cc51e7
--- /dev/null
+++ b/nfc/aidl/default/main.cpp
@@ -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.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Nfc.h"
+using ::aidl::android::hardware::nfc::Nfc;
+
+int main() {
+ LOG(INFO) << "NFC HAL starting up";
+ if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) {
+ LOG(INFO) << "failed to set thread pool max thread count";
+ return 1;
+ }
+ std::shared_ptr<Nfc> nfc_service = ndk::SharedRefBase::make<Nfc>();
+
+ const std::string instance = std::string() + Nfc::descriptor + "/default";
+ binder_status_t status =
+ AServiceManager_addService(nfc_service->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+ ABinderProcess_joinThreadPool();
+ return 0;
+}
diff --git a/nfc/aidl/default/nfc-service-example.rc b/nfc/aidl/default/nfc-service-example.rc
new file mode 100644
index 0000000..7d7052e
--- /dev/null
+++ b/nfc/aidl/default/nfc-service-example.rc
@@ -0,0 +1,4 @@
+service nfc_hal_service /vendor/bin/hw/android.hardware.nfc-service.st
+ class hal
+ user nfc
+ group nfc
diff --git a/nfc/aidl/default/nfc-service-example.xml b/nfc/aidl/default/nfc-service-example.xml
new file mode 100644
index 0000000..70fed20
--- /dev/null
+++ b/nfc/aidl/default/nfc-service-example.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.nfc</name>
+ <fqname>INfc/default</fqname>
+ </hal>
+</manifest>
diff --git a/nfc/aidl/vts/functional/Android.bp b/nfc/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..99eecd0
--- /dev/null
+++ b/nfc/aidl/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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: "VtsAidlHalNfcTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: [
+ "VtsAidlHalNfcTargetTest.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.nfc-V1-ndk",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp
new file mode 100644
index 0000000..977b25c
--- /dev/null
+++ b/nfc/aidl/vts/functional/VtsAidlHalNfcTargetTest.cpp
@@ -0,0 +1,458 @@
+/*
+ * 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 "nfc_aidl_hal_test"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/nfc/BnNfc.h>
+#include <aidl/android/hardware/nfc/BnNfcClientCallback.h>
+#include <aidl/android/hardware/nfc/INfc.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.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 <chrono>
+#include <future>
+
+using aidl::android::hardware::nfc::INfc;
+using aidl::android::hardware::nfc::INfcClientCallback;
+using aidl::android::hardware::nfc::NfcCloseType;
+using aidl::android::hardware::nfc::NfcConfig;
+using aidl::android::hardware::nfc::NfcEvent;
+using aidl::android::hardware::nfc::NfcStatus;
+using aidl::android::hardware::nfc::PresenceCheckAlgorithm;
+
+using android::getAidlHalInstanceNames;
+using android::PrintInstanceNameToString;
+using android::base::StringPrintf;
+using ndk::enum_range;
+using ndk::ScopedAStatus;
+using ndk::SharedRefBase;
+using ndk::SpAIBinder;
+
+constexpr static int kCallbackTimeoutMs = 10000;
+
+// 261 bytes is the default and minimum transceive length
+constexpr unsigned int MIN_ISO_DEP_TRANSCEIVE_LENGTH = 261;
+
+// Range of valid off host route ids
+constexpr uint8_t MIN_OFFHOST_ROUTE_ID = 0x01;
+constexpr uint8_t MAX_OFFHOST_ROUTE_ID = 0xFE;
+
+class NfcClientCallback : public aidl::android::hardware::nfc::BnNfcClientCallback {
+ public:
+ NfcClientCallback(const std::function<void(NfcEvent, NfcStatus)>& on_hal_event_cb,
+ const std::function<void(const std::vector<uint8_t>&)>& on_nci_data_cb)
+ : on_nci_data_cb_(on_nci_data_cb), on_hal_event_cb_(on_hal_event_cb) {}
+ virtual ~NfcClientCallback() = default;
+
+ ::ndk::ScopedAStatus sendEvent(NfcEvent event, NfcStatus event_status) override {
+ on_hal_event_cb_(event, event_status);
+ return ::ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus sendData(const std::vector<uint8_t>& data) override {
+ on_nci_data_cb_(data);
+ return ::ndk::ScopedAStatus::ok();
+ };
+
+ private:
+ std::function<void(const std::vector<uint8_t>&)> on_nci_data_cb_;
+ std::function<void(NfcEvent, NfcStatus)> on_hal_event_cb_;
+};
+
+class NfcAidl : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+ infc_ = INfc::fromBinder(binder);
+ ASSERT_NE(infc_, nullptr);
+ }
+ std::shared_ptr<INfc> infc_;
+};
+
+/*
+ * OpenAndCloseForDisable:
+ * Makes an open call, waits for NfcEvent::OPEN_CPLT
+ * Immediately calls close(NfcCloseType::DISABLE) and
+ * waits for NfcEvent::CLOSE_CPLT
+ *
+ */
+TEST_P(NfcAidl, OpenAndCloseForDisable) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ LOG(INFO) << StringPrintf("%s,%d ", __func__, event);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close DISABLE";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ LOG(INFO) << "wait for close";
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * OpenAndCloseForHostSwitchedOff:
+ * Makes an open call, waits for NfcEvent::OPEN_CPLT
+ * Immediately calls close(NfcCloseType::HOST_SWITCHED_OFF) and
+ * waits for NfcEvent::CLOSE_CPLT
+ *
+ */
+TEST_P(NfcAidl, OpenAndCloseForHostSwitchedOff) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close HOST_SWITCHED_OFF";
+ EXPECT_TRUE(infc_->close(NfcCloseType::HOST_SWITCHED_OFF).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * OpenAfterOpen:
+ * Calls open() multiple times
+ * Checks status
+ */
+TEST_P(NfcAidl, OpenAfterOpen) {
+ int open_count = 0;
+ std::promise<void> open_cb_promise;
+ std::promise<void> open2_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto open2_cb_future = open2_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &open2_cb_promise, &open_count](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) {
+ open_count == 0 ? open_cb_promise.set_value() : open2_cb_promise.set_value();
+ open_count++;
+ }
+ },
+ [](auto) {});
+
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Open again and wait for OPEN_CPLT
+ LOG(INFO) << "open again";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open2_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * CloseAfterClose:
+ * Calls close() multiple times
+ * Checks status
+ */
+TEST_P(NfcAidl, CloseAfterClose) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+ // Close again should fail.
+ LOG(INFO) << "close again";
+ EXPECT_TRUE(!(infc_->close(NfcCloseType::DISABLE).isOk()));
+}
+
+/*
+ * PowerCycleAfterOpen:
+ * Calls powerCycle() after open
+ * Waits for NfcEvent.OPEN_CPLT
+ * Checks status
+ */
+TEST_P(NfcAidl, PowerCycleAfterOpen) {
+ int open_cplt_count = 0;
+ std::promise<void> open_cb_promise;
+ std::promise<void> power_cycle_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto power_cycle_cb_future = power_cycle_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise, &power_cycle_cb_promise, &open_cplt_count](
+ auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) {
+ if (open_cplt_count == 0) {
+ open_cplt_count++;
+ open_cb_promise.set_value();
+ } else {
+ power_cycle_cb_promise.set_value();
+ }
+ }
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // PowerCycle and wait for OPEN_CPLT
+ LOG(INFO) << "PowerCycle";
+ EXPECT_TRUE(infc_->powerCycle().isOk());
+ EXPECT_EQ(power_cycle_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * PowerCycleAfterClose:
+ * Calls powerCycle() after close
+ * PowerCycle should fail immediately
+ */
+TEST_P(NfcAidl, PowerCycleAfterClose) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // PowerCycle should fail
+ LOG(INFO) << "PowerCycle";
+ EXPECT_TRUE(!(infc_->powerCycle().isOk()));
+}
+
+/*
+ * CoreInitializedAfterOpen:
+ * Calls coreInitialized() after open
+ * Waits for NfcEvent.POST_INIT_CPLT
+ */
+TEST_P(NfcAidl, CoreInitializedAfterOpen) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> core_init_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto core_init_cb_future = core_init_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise, &core_init_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::POST_INIT_CPLT) core_init_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // CoreInitialized and wait for POST_INIT_CPLT
+ LOG(INFO) << "coreInitialized";
+ EXPECT_TRUE(infc_->coreInitialized().isOk());
+ EXPECT_EQ(core_init_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+}
+
+/*
+ * CoreInitializedAfterClose:
+ * Calls coreInitialized() after close
+ * coreInitialized() should fail immediately
+ */
+TEST_P(NfcAidl, CoreInitializedAfterClose) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // coreInitialized should fail
+ LOG(INFO) << "CoreInitialized";
+ EXPECT_TRUE(!(infc_->coreInitialized().isOk()));
+}
+
+/*
+ * PreDiscoverAfterClose:
+ * Call preDiscover() after close
+ * preDiscover() should fail immediately
+ */
+TEST_P(NfcAidl, PreDiscoverAfterClose) {
+ std::promise<void> open_cb_promise;
+ std::promise<void> close_cb_promise;
+ auto open_cb_future = open_cb_promise.get_future();
+ auto close_cb_future = close_cb_promise.get_future();
+ std::shared_ptr<INfcClientCallback> mCallback = ::ndk::SharedRefBase::make<NfcClientCallback>(
+ [&open_cb_promise, &close_cb_promise](auto event, auto status) {
+ EXPECT_EQ(status, NfcStatus::OK);
+ if (event == NfcEvent::OPEN_CPLT) open_cb_promise.set_value();
+ if (event == NfcEvent::CLOSE_CPLT) close_cb_promise.set_value();
+ },
+ [](auto) {});
+ std::chrono::milliseconds timeout{kCallbackTimeoutMs};
+ // Open and wait for OPEN_CPLT
+ LOG(INFO) << "open";
+ EXPECT_TRUE(infc_->open(mCallback).isOk());
+ EXPECT_EQ(open_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // Close and wait for CLOSE_CPLT
+ LOG(INFO) << "close";
+ EXPECT_TRUE(infc_->close(NfcCloseType::DISABLE).isOk());
+ EXPECT_EQ(close_cb_future.wait_for(timeout), std::future_status::ready);
+
+ // preDiscover should fail
+ LOG(INFO) << "preDiscover";
+ EXPECT_TRUE(!(infc_->preDiscover().isOk()));
+}
+
+/*
+ * checkGetConfigValues:
+ * Calls getConfig()
+ * checks if fields in NfcConfig are populated correctly
+ */
+TEST_P(NfcAidl, CheckGetConfigValues) {
+ NfcConfig configValue;
+ EXPECT_TRUE(infc_->getConfig(&configValue).isOk());
+ EXPECT_GE(configValue.maxIsoDepTransceiveLength, MIN_ISO_DEP_TRANSCEIVE_LENGTH);
+ LOG(INFO) << StringPrintf("configValue.maxIsoDepTransceiveLength = %x",
+ configValue.maxIsoDepTransceiveLength);
+ for (auto uicc : configValue.offHostRouteUicc) {
+ LOG(INFO) << StringPrintf("offHostRouteUicc = %x", uicc);
+ EXPECT_GE(uicc, MIN_OFFHOST_ROUTE_ID);
+ EXPECT_LE(uicc, MAX_OFFHOST_ROUTE_ID);
+ }
+ for (auto ese : configValue.offHostRouteEse) {
+ LOG(INFO) << StringPrintf("offHostRouteEse = %x", ese);
+ EXPECT_GE(ese, MIN_OFFHOST_ROUTE_ID);
+ EXPECT_LE(ese, MAX_OFFHOST_ROUTE_ID);
+ }
+ if (configValue.defaultIsoDepRoute != 0) {
+ EXPECT_GE((uint8_t)configValue.defaultIsoDepRoute, MIN_OFFHOST_ROUTE_ID);
+ EXPECT_LE((uint8_t)configValue.defaultIsoDepRoute, MAX_OFFHOST_ROUTE_ID);
+ }
+}
+
+/*
+ * CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging:
+ * Calls setEnableVerboseLogging()
+ * checks the return value of isVerboseLoggingEnabled
+ */
+TEST_P(NfcAidl, CheckisVerboseLoggingEnabledAfterSetEnableVerboseLogging) {
+ bool enabled = false;
+ EXPECT_TRUE(infc_->setEnableVerboseLogging(true).isOk());
+ EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk());
+ EXPECT_TRUE(enabled);
+ EXPECT_TRUE(infc_->setEnableVerboseLogging(false).isOk());
+ EXPECT_TRUE(infc_->isVerboseLoggingEnabled(&enabled).isOk());
+ EXPECT_TRUE(!enabled);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcAidl);
+INSTANTIATE_TEST_SUITE_P(Nfc, NfcAidl,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(INfc::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_startThreadPool();
+ std::system("/system/bin/svc nfc disable"); /* Turn off NFC */
+ sleep(5);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ std::system("/system/bin/svc nfc enable"); /* Turn on NFC */
+ sleep(5);
+ return status;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
index 9846ee9..4b63799 100644
--- a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -96,7 +96,9 @@
* - TRUSTED_ENVRIONMENT IKeyMintDevices must support curve 25519 for Purpose::SIGN (Ed25519,
* as specified in RFC 8032), Purpose::ATTEST_KEY (Ed25519) or for KeyPurpose::AGREE_KEY
* (X25519, as specified in RFC 7748). However, a key must have exactly one of these
- * purpose values; the same key cannot be used for multiple purposes.
+ * purpose values; the same key cannot be used for multiple purposes. Signing operations
+ * (Purpose::SIGN) have a message size limit of 16 KiB; operations on messages longer than
+ * this limit must fail with ErrorCode::INVALID_INPUT_LENGTH.
* STRONGBOX IKeyMintDevices do not support curve 25519.
*
* o AES
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
index 374f2da..146a527 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -25,6 +25,7 @@
#include <cppbor_parse.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
+#include <openssl/evp.h>
#include <openssl/mem.h>
#include <remote_prov/remote_prov_utils.h>
@@ -206,6 +207,21 @@
return boot_patch_level(key_characteristics_);
}
+bool KeyMintAidlTestBase::Curve25519Supported() {
+ // Strongbox never supports curve 25519.
+ if (SecLevel() == SecurityLevel::STRONGBOX) {
+ return false;
+ }
+
+ // Curve 25519 was included in version 2 of the KeyMint interface.
+ int32_t version = 0;
+ auto status = keymint_->getInterfaceVersion(&version);
+ if (!status.isOk()) {
+ ADD_FAILURE() << "Failed to determine interface version";
+ }
+ return version >= 2;
+}
+
ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
if (result.isOk()) return ErrorCode::OK;
@@ -543,7 +559,12 @@
std::vector<uint8_t> o_put;
result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put);
- if (result.isOk()) output->append(o_put.begin(), o_put.end());
+ if (result.isOk()) {
+ output->append(o_put.begin(), o_put.end());
+ } else {
+ // Failure always terminates the operation.
+ op_ = {};
+ }
return GetReturnErrorCode(result);
}
@@ -740,6 +761,19 @@
if (digest == Digest::NONE) {
switch (EVP_PKEY_id(pub_key.get())) {
+ case EVP_PKEY_ED25519: {
+ ASSERT_EQ(64, signature.size());
+ uint8_t pub_keydata[32];
+ size_t pub_len = sizeof(pub_keydata);
+ ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(pub_key.get(), pub_keydata, &pub_len));
+ ASSERT_EQ(sizeof(pub_keydata), pub_len);
+ ASSERT_EQ(1, ED25519_verify(reinterpret_cast<const uint8_t*>(message.data()),
+ message.size(),
+ reinterpret_cast<const uint8_t*>(signature.data()),
+ pub_keydata));
+ break;
+ }
+
case EVP_PKEY_EC: {
vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8);
size_t data_size = std::min(data.size(), message.size());
@@ -1166,16 +1200,31 @@
vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
if (securityLevel_ == SecurityLevel::STRONGBOX) {
return {EcCurve::P_256};
+ } else if (Curve25519Supported()) {
+ return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521,
+ EcCurve::CURVE_25519};
} else {
- return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521};
+ return {
+ EcCurve::P_224,
+ EcCurve::P_256,
+ EcCurve::P_384,
+ EcCurve::P_521,
+ };
}
}
vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() {
if (SecLevel() == SecurityLevel::STRONGBOX) {
- return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521};
+ // Curve 25519 is not supported, either because:
+ // - KeyMint v1: it's an unknown enum value
+ // - KeyMint v2+: it's not supported by StrongBox.
+ return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521, EcCurve::CURVE_25519};
} else {
- return {};
+ if (Curve25519Supported()) {
+ return {};
+ } else {
+ return {EcCurve::CURVE_25519};
+ }
}
}
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
index 61f9d4d..27cb99c 100644
--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -80,6 +80,8 @@
uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics);
uint32_t boot_patch_level();
+ bool Curve25519Supported();
+
ErrorCode GetReturnErrorCode(const Status& result);
ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
index 340010f..7357ac7 100644
--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -22,6 +22,7 @@
#include <algorithm>
#include <iostream>
+#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
@@ -69,6 +70,9 @@
namespace {
+// Maximum supported Ed25519 message size.
+const size_t MAX_ED25519_MSG_SIZE = 16 * 1024;
+
// Whether to check that BOOT_PATCHLEVEL is populated.
bool check_boot_pl = true;
@@ -415,6 +419,126 @@
// } end SEQUENCE (PrivateKeyInfo)
);
+/**
+ * Ed25519 key pair generated as follows:
+ * ```
+ * % openssl req -x509 -newkey ED25519 -days 700 -nodes \
+ * -keyout ed25519_priv.key -out ed25519.pem * -subj "/CN=fake.ed25519.com"
+ * Generating a ED25519 private key writing new private key to
+ * 'ed25519_priv.key'
+ * -----
+ * % cat ed25519_priv.key
+ * -----BEGIN PRIVATE KEY-----
+ * MC4CAQAwBQYDK2VwBCIEIKl3A5quNywcj1P+0XI9SBalFPIvO52NxceMLRH6dVmR
+ * -----END PRIVATE KEY-----
+ * % der2ascii -pem -i ed25519_priv.key
+ * SEQUENCE {
+ * INTEGER { 0 }
+ * SEQUENCE {
+ * # ed25519
+ * OBJECT_IDENTIFIER { 1.3.101.112 }
+ * }
+ * OCTET_STRING {
+ * OCTET_STRING { `a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991` }
+ * }
+ * }
+ * % cat ed25519.pem
+ * -----BEGIN CERTIFICATE-----
+ * MIIBSjCB/aADAgECAhR0Jron3eKcdgqyecv/eEfGWAzn8DAFBgMrZXAwGzEZMBcG
+ * A1UEAwwQZmFrZS5lZDI1NTE5LmNvbTAeFw0yMTEwMjAwODI3NDJaFw0yMzA5MjAw
+ * ODI3NDJaMBsxGTAXBgNVBAMMEGZha2UuZWQyNTUxOS5jb20wKjAFBgMrZXADIQDv
+ * uwHz+3TaQ69D2digxlz0fFfsZg0rPqgQae3jBPRWkaNTMFEwHQYDVR0OBBYEFN9O
+ * od30SY4JTs66ZR403UPya+iXMB8GA1UdIwQYMBaAFN9Ood30SY4JTs66ZR403UPy
+ * a+iXMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EAKjVrYQjuE/gEL2j/ABpDbFjV
+ * Ilg5tJ6MN/P3psAv3Cs7f0X1lFqdlt15nJ/6aj2cmGCwNRXt5wcyYDKNu+v2Dw==
+ * -----END CERTIFICATE-----
+ * % openssl x509 -in ed25519.pem -text -noout
+ * Certificate:
+ * Data:
+ * Version: 3 (0x2)
+ * Serial Number:
+ * 74:26:ba:27:dd:e2:9c:76:0a:b2:79:cb:ff:78:47:c6:58:0c:e7:f0
+ * Signature Algorithm: ED25519
+ * Issuer: CN = fake.ed25519.com
+ * Validity
+ * Not Before: Oct 20 08:27:42 2021 GMT
+ * Not After : Sep 20 08:27:42 2023 GMT
+ * Subject: CN = fake.ed25519.com
+ * Subject Public Key Info:
+ * Public Key Algorithm: ED25519
+ * ED25519 Public-Key:
+ * pub:
+ * ef:bb:01:f3:fb:74:da:43:af:43:d9:d8:a0:c6:5c:
+ * f4:7c:57:ec:66:0d:2b:3e:a8:10:69:ed:e3:04:f4:
+ * 56:91
+ * X509v3 extensions:
+ * X509v3 Subject Key Identifier:
+ * DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
+ * X509v3 Authority Key Identifier:
+ * keyid:DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
+ *
+ * X509v3 Basic Constraints: critical
+ * CA:TRUE
+ * Signature Algorithm: ED25519
+ * 2a:35:6b:61:08:ee:13:f8:04:2f:68:ff:00:1a:43:6c:58:d5:
+ * 22:58:39:b4:9e:8c:37:f3:f7:a6:c0:2f:dc:2b:3b:7f:45:f5:
+ * 94:5a:9d:96:dd:79:9c:9f:fa:6a:3d:9c:98:60:b0:35:15:ed:
+ * e7:07:32:60:32:8d:bb:eb:f6:0f
+ * ```
+ */
+string ed25519_key = hex2str("a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991");
+string ed25519_pkcs8_key = hex2str(
+ // RFC 5208 s5
+ "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) {
+ "0201" // INTEGER length 1 (Version)
+ "00" // version 0
+ "3005" // SEQUENCE length 05 (AlgorithmIdentifier) {
+ "0603" // OBJECT IDENTIFIER length 3 (algorithm)
+ "2b6570" // 1.3.101.112 (id-Ed125519 RFC 8410 s3)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0422" // OCTET STRING length 0x22 (PrivateKey)
+ "0420" // OCTET STRING length 0x20 (RFC 8410 s7)
+ "a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991"
+ // } end SEQUENCE (PrivateKeyInfo)
+);
+string ed25519_pubkey = hex2str("efbb01f3fb74da43af43d9d8a0c65cf47c57ec660d2b3ea81069ede304f45691");
+
+/**
+ * X25519 key pair generated as follows:
+ * ```
+ * % openssl genpkey -algorithm X25519 > x25519_priv.key
+ * % cat x25519_priv.key
+ * -----BEGIN PRIVATE KEY-----
+ * MC4CAQAwBQYDK2VuBCIEIGgPwF3NLwQx/Sfwr2nfJvXitwlDNh3Skzh+TISN/y1C
+ * -----END PRIVATE KEY-----
+ * % der2ascii -pem -i x25519_priv.key
+ * SEQUENCE {
+ * INTEGER { 0 }
+ * SEQUENCE {
+ * # x25519
+ * OBJECT_IDENTIFIER { 1.3.101.110 }
+ * }
+ * OCTET_STRING {
+ * OCTET_STRING { `680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42` }
+ * }
+ * }
+ * ```
+ */
+
+string x25519_key = hex2str("680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
+string x25519_pkcs8_key = hex2str(
+ // RFC 5208 s5
+ "302e" // SEQUENCE length 0x2e (PrivateKeyInfo) {
+ "0201" // INTEGER length 1 (Version)
+ "00" // version 0
+ "3005" // SEQUENCE length 05 (AlgorithmIdentifier) {
+ "0603" // OBJECT IDENTIFIER length 3 (algorithm)
+ "2b656e" // 1.3.101.110 (id-X125519 RFC 8410 s3)
+ "0422" // OCTET STRING length 0x22 (PrivateKey)
+ "0420" // OCTET STRING length 0x20 (RFC 8410 s7)
+ "680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
+string x25519_pubkey = hex2str("be46925a857f17831d6d454b9d3d36a4a30166edf80eb82b684661c3e258f768");
+
struct RSA_Delete {
void operator()(RSA* p) { RSA_free(p); }
};
@@ -1374,7 +1498,7 @@
/*
* NewKeyGenerationTest.Ecdsa
*
- * Verifies that keymint can generate all required EC key sizes, and that the resulting keys
+ * Verifies that keymint can generate all required EC curves, and that the resulting keys
* have correct characteristics.
*/
TEST_P(NewKeyGenerationTest, Ecdsa) {
@@ -1400,6 +1524,65 @@
}
/*
+ * NewKeyGenerationTest.EcdsaCurve25519
+ *
+ * Verifies that keymint can generate a curve25519 key, and that the resulting key
+ * has correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaCurve25519) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics);
+ ASSERT_EQ(result, ErrorCode::OK);
+ ASSERT_GT(key_blob.size(), 0U);
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+
+ CheckBaseParams(key_characteristics);
+ CheckCharacteristics(key_blob, key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+ CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.EcCurve25519MultiPurposeFail
+ *
+ * Verifies that KeyMint rejects an attempt to generate a curve 25519 key for both
+ * SIGN and AGREE_KEY.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaCurve25519MultiPurposeFail) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics);
+ ASSERT_EQ(result, ErrorCode::INCOMPATIBLE_PURPOSE);
+}
+
+/*
* NewKeyGenerationTest.EcdsaAttestation
*
* Verifies that for all Ecdsa key sizes, if challenge and app id is provided,
@@ -1453,6 +1636,62 @@
}
/*
+ * NewKeyGenerationTest.EcdsaAttestationCurve25519
+ *
+ * Verifies that for a curve 25519 key, if challenge and app id is provided,
+ * an attestation will be generated.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationCurve25519) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ auto challenge = "hello";
+ auto app_id = "foo";
+
+ auto subject = "cert subj 2";
+ vector<uint8_t> subject_der(make_name_from_str(subject));
+
+ uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
+ vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+ vector<uint8_t> key_blob;
+ vector<KeyCharacteristics> key_characteristics;
+ ErrorCode result = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .AttestationChallenge(challenge)
+ .AttestationApplicationId(app_id)
+ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics);
+ ASSERT_EQ(ErrorCode::OK, result);
+ ASSERT_GT(key_blob.size(), 0U);
+ CheckBaseParams(key_characteristics);
+ CheckCharacteristics(key_blob, key_characteristics);
+
+ AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+ EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+ EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+ EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+ ASSERT_GT(cert_chain_.size(), 0);
+ verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+
+ AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+ AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+ EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
+ sw_enforced, hw_enforced, SecLevel(),
+ cert_chain_[0].encodedCertificate));
+
+ CheckedDeleteKey(&key_blob);
+}
+
+/*
* NewKeyGenerationTest.EcdsaAttestationTags
*
* Verifies that creation of an attested ECDSA key includes various tags in the
@@ -1984,20 +2223,22 @@
}
/*
- * NewKeyGenerationTest.EcdsaInvalidSize
+ * NewKeyGenerationTest.EcdsaInvalidCurve
*
- * Verifies that specifying an invalid key size for EC key generation returns
+ * Verifies that specifying an invalid curve for EC key generation returns
* UNSUPPORTED_KEY_SIZE.
*/
-TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) {
+TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) {
for (auto curve : InvalidCurves()) {
vector<uint8_t> key_blob;
vector<KeyCharacteristics> key_characteristics;
- ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder()
- .EcdsaSigningKey(curve)
- .Digest(Digest::NONE)
- .SetDefaultValidity(),
- &key_blob, &key_characteristics));
+ auto result = GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ &key_blob, &key_characteristics);
+ ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE ||
+ result == ErrorCode::UNSUPPORTED_EC_CURVE);
}
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
@@ -2808,15 +3049,19 @@
/*
* SigningOperationsTest.EcdsaAllDigestsAndCurves
*
- * Verifies ECDSA signature/verification for all digests and curves.
+ * Verifies ECDSA signature/verification for all digests and required curves.
*/
TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) {
- auto digests = ValidDigests(true /* withNone */, false /* withMD5 */);
string message = "1234567890";
string corrupt_message = "2234567890";
for (auto curve : ValidCurves()) {
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
+ // Ed25519 only allows Digest::NONE.
+ auto digests = (curve == EcCurve::CURVE_25519)
+ ? std::vector<Digest>(1, Digest::NONE)
+ : ValidDigests(true /* withNone */, false /* withMD5 */);
+
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(curve)
@@ -2841,25 +3086,141 @@
/*
* SigningOperationsTest.EcdsaAllCurves
*
- * Verifies that ECDSA operations succeed with all possible curves.
+ * Verifies that ECDSA operations succeed with all required curves.
*/
TEST_P(SigningOperationsTest, EcdsaAllCurves) {
for (auto curve : ValidCurves()) {
+ Digest digest = (curve == EcCurve::CURVE_25519 ? Digest::NONE : Digest::SHA_2_256);
+ SCOPED_TRACE(testing::Message() << "Curve::" << curve);
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
.Authorization(TAG_NO_AUTH_REQUIRED)
.EcdsaSigningKey(curve)
- .Digest(Digest::SHA_2_256)
+ .Digest(digest)
.SetDefaultValidity());
EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
if (error != ErrorCode::OK) continue;
string message(1024, 'a');
- SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+ SignMessage(message, AuthorizationSetBuilder().Digest(digest));
CheckedDeleteKey();
}
}
/*
+ * SigningOperationsTest.EcdsaCurve25519
+ *
+ * Verifies that ECDSA operations succeed with curve25519.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity());
+ ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+ string message(1024, 'a');
+ SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
+ CheckedDeleteKey();
+}
+
+/*
+ * SigningOperationsTest.EcdsaCurve25519MaxSize
+ *
+ * Verifies that EDDSA operations with curve25519 under the maximum message size succeed.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSize) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity());
+ ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+ auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+
+ for (size_t msg_size : {MAX_ED25519_MSG_SIZE - 1, MAX_ED25519_MSG_SIZE}) {
+ SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
+ string message(msg_size, 'a');
+
+ // Attempt to sign via Begin+Finish.
+ AuthorizationSet out_params;
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+ EXPECT_TRUE(out_params.empty());
+ string signature;
+ auto result = Finish(message, &signature);
+ EXPECT_EQ(result, ErrorCode::OK);
+ LocalVerifyMessage(message, signature, params);
+
+ // Attempt to sign via Begin+Update+Finish
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+ EXPECT_TRUE(out_params.empty());
+ string output;
+ result = Update(message, &output);
+ EXPECT_EQ(result, ErrorCode::OK);
+ EXPECT_EQ(output.size(), 0);
+ string signature2;
+ EXPECT_EQ(ErrorCode::OK, Finish({}, &signature2));
+ LocalVerifyMessage(message, signature2, params);
+ }
+
+ CheckedDeleteKey();
+}
+
+/*
+ * SigningOperationsTest.EcdsaCurve25519MaxSizeFail
+ *
+ * Verifies that EDDSA operations with curve25519 fail when message size is too large.
+ */
+TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSizeFail) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ EcCurve curve = EcCurve::CURVE_25519;
+ ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(curve)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity());
+ ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+
+ auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+
+ for (size_t msg_size : {MAX_ED25519_MSG_SIZE + 1, MAX_ED25519_MSG_SIZE * 2}) {
+ SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
+ string message(msg_size, 'a');
+
+ // Attempt to sign via Begin+Finish.
+ AuthorizationSet out_params;
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+ EXPECT_TRUE(out_params.empty());
+ string signature;
+ auto result = Finish(message, &signature);
+ EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
+
+ // Attempt to sign via Begin+Update (but never get to Finish)
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
+ EXPECT_TRUE(out_params.empty());
+ string output;
+ result = Update(message, &output);
+ EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
+ }
+
+ CheckedDeleteKey();
+}
+
+/*
* SigningOperationsTest.EcdsaNoDigestHugeData
*
* Verifies that ECDSA operations support very large messages, even without digesting. This
@@ -3509,6 +3870,255 @@
}
/*
+ * ImportKeyTest.Ed25519RawSuccess
+ *
+ * Verifies that importing and using a raw Ed25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, Ed25519RawSuccess) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, ed25519_key));
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+ CheckOrigin();
+
+ // The returned cert should hold the correct public key.
+ ASSERT_GT(cert_chain_.size(), 0);
+ X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_NE(kmKeyCert, nullptr);
+ EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+ ASSERT_NE(kmPubKey.get(), nullptr);
+ size_t kmPubKeySize = 32;
+ uint8_t kmPubKeyData[32];
+ ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+ ASSERT_EQ(kmPubKeySize, 32);
+ EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
+
+ string message(32, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+ string signature = SignMessage(message, params);
+ LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ed25519Pkcs8Success
+ *
+ * Verifies that importing and using a PKCS#8-encoded Ed25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, Ed25519Pkcs8Success) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, ed25519_pkcs8_key));
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+ CheckOrigin();
+
+ // The returned cert should hold the correct public key.
+ ASSERT_GT(cert_chain_.size(), 0);
+ X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_NE(kmKeyCert, nullptr);
+ EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+ ASSERT_NE(kmPubKey.get(), nullptr);
+ size_t kmPubKeySize = 32;
+ uint8_t kmPubKeyData[32];
+ ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+ ASSERT_EQ(kmPubKeySize, 32);
+ EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
+
+ string message(32, 'a');
+ auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+ string signature = SignMessage(message, params);
+ LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ed25519CurveMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, Ed25519CurveMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_NE(ErrorCode::OK,
+ ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, ed25519_key));
+}
+
+/*
+ * ImportKeyTest.Ed25519FormatMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, Ed25519FormatMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, ed25519_key));
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, ed25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.Ed25519PurposeMismatch
+ *
+ * Verifies that importing an Ed25519 key pair with an invalid purpose fails.
+ */
+TEST_P(ImportKeyTest, Ed25519PurposeMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ // Can't have both SIGN and ATTEST_KEY
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, ed25519_key));
+ // AGREE_KEY is for X25519 (but can only tell the difference if the import key is in
+ // PKCS#8 format and so includes an OID).
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .Digest(Digest::NONE)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, ed25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.X25519RawSuccess
+ *
+ * Verifies that importing and using a raw X25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, X25519RawSuccess) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, x25519_key));
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+ CheckOrigin();
+}
+
+/*
+ * ImportKeyTest.X25519Pkcs8Success
+ *
+ * Verifies that importing and using a PKCS#8-encoded X25519 private key works correctly.
+ */
+TEST_P(ImportKeyTest, X25519Pkcs8Success) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, x25519_pkcs8_key));
+
+ CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+ CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
+ CheckOrigin();
+}
+
+/*
+ * ImportKeyTest.X25519CurveMismatch
+ *
+ * Verifies that importing an X25519 key with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, X25519CurveMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::P_224 /* Doesn't match key */)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, x25519_key));
+}
+
+/*
+ * ImportKeyTest.X25519FormatMismatch
+ *
+ * Verifies that importing an X25519 key with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, X25519FormatMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, x25519_key));
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::RAW, x25519_pkcs8_key));
+}
+
+/*
+ * ImportKeyTest.X25519PurposeMismatch
+ *
+ * Verifies that importing an X25519 key pair with an invalid format fails.
+ */
+TEST_P(ImportKeyTest, X25519PurposeMismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, x25519_pkcs8_key));
+ ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(EcCurve::CURVE_25519)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, x25519_pkcs8_key));
+}
+
+/*
* ImportKeyTest.AesSuccess
*
* Verifies that importing and using an AES key works.
@@ -6703,8 +7313,6 @@
INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
-typedef KeyMintAidlTestBase KeyAgreementTest;
-
static int EcdhCurveToOpenSslCurveName(EcCurve curve) {
switch (curve) {
case EcCurve::P_224:
@@ -6720,10 +7328,108 @@
}
}
+class KeyAgreementTest : public KeyMintAidlTestBase {
+ protected:
+ void GenerateLocalEcKey(EcCurve localCurve, EVP_PKEY_Ptr* localPrivKey,
+ std::vector<uint8_t>* localPublicKey) {
+ // Generate EC key locally (with access to private key material)
+ if (localCurve == EcCurve::CURVE_25519) {
+ uint8_t privKeyData[32];
+ uint8_t pubKeyData[32];
+ X25519_keypair(pubKeyData, privKeyData);
+ *localPublicKey = vector<uint8_t>(pubKeyData, pubKeyData + 32);
+ *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new_raw_private_key(
+ EVP_PKEY_X25519, nullptr, privKeyData, sizeof(privKeyData)));
+ } else {
+ auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+ int curveName = EcdhCurveToOpenSslCurveName(localCurve);
+ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
+ ASSERT_NE(group, nullptr);
+ ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+ ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
+ *localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+ ASSERT_EQ(EVP_PKEY_set1_EC_KEY(localPrivKey->get(), ecKey.get()), 1);
+
+ // Get encoded form of the public part of the locally generated key...
+ unsigned char* p = nullptr;
+ int localPublicKeySize = i2d_PUBKEY(localPrivKey->get(), &p);
+ ASSERT_GT(localPublicKeySize, 0);
+ *localPublicKey =
+ vector<uint8_t>(reinterpret_cast<const uint8_t*>(p),
+ reinterpret_cast<const uint8_t*>(p + localPublicKeySize));
+ OPENSSL_free(p);
+ }
+ }
+
+ void GenerateKeyMintEcKey(EcCurve curve, EVP_PKEY_Ptr* kmPubKey) {
+ vector<uint8_t> challenge = {0x41, 0x42};
+ ErrorCode result =
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_EC_CURVE, curve)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .Authorization(TAG_ALGORITHM, Algorithm::EC)
+ .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
+ .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+ .SetDefaultValidity());
+ ASSERT_EQ(ErrorCode::OK, result) << "Failed to generate key";
+ ASSERT_GT(cert_chain_.size(), 0);
+ X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_NE(kmKeyCert, nullptr);
+ // Check that keyAgreement (bit 4) is set in KeyUsage
+ EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
+ *kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
+ ASSERT_NE(*kmPubKey, nullptr);
+ if (dump_Attestations) {
+ for (size_t n = 0; n < cert_chain_.size(); n++) {
+ std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
+ }
+ }
+ }
+
+ void CheckAgreement(EVP_PKEY_Ptr kmPubKey, EVP_PKEY_Ptr localPrivKey,
+ const std::vector<uint8_t>& localPublicKey) {
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+ string ZabFromKeyMintStr;
+ ASSERT_EQ(ErrorCode::OK,
+ Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr));
+ vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
+ vector<uint8_t> ZabFromTest;
+
+ if (EVP_PKEY_id(kmPubKey.get()) == EVP_PKEY_X25519) {
+ size_t kmPubKeySize = 32;
+ uint8_t kmPubKeyData[32];
+ ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+ ASSERT_EQ(kmPubKeySize, 32);
+
+ uint8_t localPrivKeyData[32];
+ size_t localPrivKeySize = 32;
+ ASSERT_EQ(1, EVP_PKEY_get_raw_private_key(localPrivKey.get(), localPrivKeyData,
+ &localPrivKeySize));
+ ASSERT_EQ(localPrivKeySize, 32);
+
+ uint8_t sharedKey[32];
+ ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData));
+ ZabFromTest = std::vector<uint8_t>(sharedKey, sharedKey + 32);
+ } else {
+ // Perform local ECDH between the two keys so we can check if we get the same Zab..
+ auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(localPrivKey.get(), nullptr));
+ ASSERT_NE(ctx, nullptr);
+ ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
+ ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPubKey.get()), 1);
+ size_t ZabFromTestLen = 0;
+ ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
+ ZabFromTest.resize(ZabFromTestLen);
+ ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
+ }
+ EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
+ }
+};
+
/*
* KeyAgreementTest.Ecdh
*
- * Verifies that ECDH works for all curves
+ * Verifies that ECDH works for all required curves
*/
TEST_P(KeyAgreementTest, Ecdh) {
// Because it's possible to use this API with keys on different curves, we
@@ -6737,49 +7443,13 @@
for (auto curve : ValidCurves()) {
for (auto localCurve : ValidCurves()) {
// Generate EC key locally (with access to private key material)
- auto ecKey = EC_KEY_Ptr(EC_KEY_new());
- int curveName = EcdhCurveToOpenSslCurveName(localCurve);
- auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
- ASSERT_NE(group, nullptr);
- ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
- ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
- auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
- ASSERT_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()), 1);
-
- // Get encoded form of the public part of the locally generated key...
- unsigned char* p = nullptr;
- int encodedPublicKeySize = i2d_PUBKEY(pkey.get(), &p);
- ASSERT_GT(encodedPublicKeySize, 0);
- vector<uint8_t> encodedPublicKey(
- reinterpret_cast<const uint8_t*>(p),
- reinterpret_cast<const uint8_t*>(p + encodedPublicKeySize));
- OPENSSL_free(p);
+ EVP_PKEY_Ptr localPrivKey;
+ vector<uint8_t> localPublicKey;
+ GenerateLocalEcKey(localCurve, &localPrivKey, &localPublicKey);
// Generate EC key in KeyMint (only access to public key material)
- vector<uint8_t> challenge = {0x41, 0x42};
- EXPECT_EQ(
- ErrorCode::OK,
- GenerateKey(AuthorizationSetBuilder()
- .Authorization(TAG_NO_AUTH_REQUIRED)
- .Authorization(TAG_EC_CURVE, curve)
- .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
- .Authorization(TAG_ALGORITHM, Algorithm::EC)
- .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
- .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
- .SetDefaultValidity()))
- << "Failed to generate key";
- ASSERT_GT(cert_chain_.size(), 0);
- X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
- ASSERT_NE(kmKeyCert, nullptr);
- // Check that keyAgreement (bit 4) is set in KeyUsage
- EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
- auto kmPkey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
- ASSERT_NE(kmPkey, nullptr);
- if (dump_Attestations) {
- for (size_t n = 0; n < cert_chain_.size(); n++) {
- std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
- }
- }
+ EVP_PKEY_Ptr kmPubKey;
+ GenerateKeyMintEcKey(curve, &kmPubKey);
// Now that we have the two keys, we ask KeyMint to perform ECDH...
if (curve != localCurve) {
@@ -6788,30 +7458,12 @@
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
string ZabFromKeyMintStr;
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
- Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+ Finish(string(localPublicKey.begin(), localPublicKey.end()),
&ZabFromKeyMintStr));
} else {
// Otherwise if the keys are using the same curve, it should work.
- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
- string ZabFromKeyMintStr;
- EXPECT_EQ(ErrorCode::OK,
- Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
- &ZabFromKeyMintStr));
- vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
-
- // Perform local ECDH between the two keys so we can check if we get the same Zab..
- auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(pkey.get(), nullptr));
- ASSERT_NE(ctx, nullptr);
- ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
- ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPkey.get()), 1);
- size_t ZabFromTestLen = 0;
- ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
- vector<uint8_t> ZabFromTest;
- ZabFromTest.resize(ZabFromTestLen);
- ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
-
- EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
+ CheckAgreement(std::move(kmPubKey), std::move(localPrivKey), localPublicKey);
}
CheckedDeleteKey();
@@ -6819,6 +7471,140 @@
}
}
+/*
+ * KeyAgreementTest.EcdhCurve25519
+ *
+ * Verifies that ECDH works for curve25519. This is also covered by the general
+ * KeyAgreementTest.Ecdh case, but is pulled out separately here because this curve was added after
+ * KeyMint 1.0.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ // Generate EC key in KeyMint (only access to public key material)
+ EcCurve curve = EcCurve::CURVE_25519;
+ EVP_PKEY_Ptr kmPubKey = nullptr;
+ GenerateKeyMintEcKey(curve, &kmPubKey);
+
+ // Generate EC key on same curve locally (with access to private key material).
+ EVP_PKEY_Ptr privKey;
+ vector<uint8_t> encodedPublicKey;
+ GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+ // Agree on a key between local and KeyMint and check it.
+ CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
+
+ CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519Imported
+ *
+ * Verifies that ECDH works for an imported curve25519 key.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519Imported) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ // Import x25519 key into KeyMint.
+ EcCurve curve = EcCurve::CURVE_25519;
+ ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .EcdsaKey(EcCurve::CURVE_25519)
+ .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+ .SetDefaultValidity(),
+ KeyFormat::PKCS8, x25519_pkcs8_key));
+ ASSERT_GT(cert_chain_.size(), 0);
+ X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+ ASSERT_NE(kmKeyCert, nullptr);
+ EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
+ ASSERT_NE(kmPubKey.get(), nullptr);
+
+ // Expect the import to emit corresponding public key data.
+ size_t kmPubKeySize = 32;
+ uint8_t kmPubKeyData[32];
+ ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
+ ASSERT_EQ(kmPubKeySize, 32);
+ EXPECT_EQ(bin2hex(std::vector<uint8_t>(kmPubKeyData, kmPubKeyData + 32)),
+ bin2hex(std::vector<uint8_t>(x25519_pubkey.begin(), x25519_pubkey.end())));
+
+ // Generate EC key on same curve locally (with access to private key material).
+ EVP_PKEY_Ptr privKey;
+ vector<uint8_t> encodedPublicKey;
+ GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+ // Agree on a key between local and KeyMint and check it.
+ CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
+
+ CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519InvalidSize
+ *
+ * Verifies that ECDH fails for curve25519 if the wrong size of public key is provided.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519InvalidSize) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ // Generate EC key in KeyMint (only access to public key material)
+ EcCurve curve = EcCurve::CURVE_25519;
+ EVP_PKEY_Ptr kmPubKey = nullptr;
+ GenerateKeyMintEcKey(curve, &kmPubKey);
+
+ // Generate EC key on same curve locally (with access to private key material).
+ EVP_PKEY_Ptr privKey;
+ vector<uint8_t> encodedPublicKey;
+ GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
+
+ ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+ string ZabFromKeyMintStr;
+ // Send in an incomplete public key.
+ ASSERT_NE(ErrorCode::OK, Finish(string(encodedPublicKey.begin(), encodedPublicKey.end() - 1),
+ &ZabFromKeyMintStr));
+
+ CheckedDeleteKey();
+}
+
+/*
+ * KeyAgreementTest.EcdhCurve25519Mismatch
+ *
+ * Verifies that ECDH fails between curve25519 and other curves.
+ */
+TEST_P(KeyAgreementTest, EcdhCurve25519Mismatch) {
+ if (!Curve25519Supported()) {
+ GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
+ }
+
+ // Generate EC key in KeyMint (only access to public key material)
+ EcCurve curve = EcCurve::CURVE_25519;
+ EVP_PKEY_Ptr kmPubKey = nullptr;
+ GenerateKeyMintEcKey(curve, &kmPubKey);
+
+ for (auto localCurve : ValidCurves()) {
+ if (localCurve == curve) {
+ continue;
+ }
+ // Generate EC key on a different curve locally (with access to private key material).
+ EVP_PKEY_Ptr privKey;
+ vector<uint8_t> encodedPublicKey;
+ GenerateLocalEcKey(localCurve, &privKey, &encodedPublicKey);
+
+ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+ string ZabFromKeyMintStr;
+ EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+ Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+ &ZabFromKeyMintStr));
+ }
+
+ CheckedDeleteKey();
+}
+
INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
using DestroyAttestationIdsTest = KeyMintAidlTestBase;
diff --git a/usb/aidl/Android.bp b/usb/aidl/Android.bp
new file mode 100644
index 0000000..d1e9e68
--- /dev/null
+++ b/usb/aidl/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+aidl_interface {
+ name: "android.hardware.usb",
+ vendor_available: true,
+ srcs: ["android/hardware/usb/*.aidl"],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.aidl
new file mode 100644
index 0000000..24c6966
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantDetectionStatus.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.usb;
+@VintfStability
+enum ContaminantDetectionStatus {
+ NOT_SUPPORTED = 0,
+ DISABLED = 1,
+ NOT_DETECTED = 2,
+ DETECTED = 3,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.aidl
new file mode 100644
index 0000000..9979869
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionMode.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.usb;
+@VintfStability
+enum ContaminantProtectionMode {
+ NONE = 0,
+ FORCE_SINK = 1,
+ FORCE_SOURCE = 2,
+ FORCE_DISABLE = 3,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.aidl
new file mode 100644
index 0000000..9642261
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/ContaminantProtectionStatus.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.usb;
+@VintfStability
+enum ContaminantProtectionStatus {
+ NONE = 0,
+ FORCE_SINK = 1,
+ FORCE_SOURCE = 2,
+ FORCE_DISABLE = 3,
+ DISABLED = 4,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl
new file mode 100644
index 0000000..73c7b82
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsb.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.usb;
+@VintfStability
+interface IUsb {
+ oneway void enableContaminantPresenceDetection(in String portName, in boolean enable, long transactionId);
+ oneway void enableUsbData(in String portName, boolean enable, long transactionId);
+ oneway void queryPortStatus(long transactionId);
+ oneway void setCallback(in android.hardware.usb.IUsbCallback callback);
+ oneway void switchRole(in String portName, in android.hardware.usb.PortRole role, long transactionId);
+ oneway void limitPowerTransfer(in String portName, boolean limit, long transactionId);
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl
new file mode 100644
index 0000000..85861e9
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/IUsbCallback.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.usb;
+@VintfStability
+interface IUsbCallback {
+ oneway void notifyPortStatusChange(in android.hardware.usb.PortStatus[] currentPortStatus, in android.hardware.usb.Status retval);
+ oneway void notifyRoleSwitchStatus(in String portName, in android.hardware.usb.PortRole newRole, in android.hardware.usb.Status retval, long transactionId);
+ oneway void notifyEnableUsbDataStatus(in String portName, boolean enable, in android.hardware.usb.Status retval, long transactionId);
+ oneway void notifyContaminantEnabledStatus(in String portName, boolean enable, in android.hardware.usb.Status retval, long transactionId);
+ oneway void notifyQueryPortStatus(in String portName, in android.hardware.usb.Status retval, long transactionId);
+ oneway void notifyLimitPowerTransferStatus(in String portName, boolean limit, in android.hardware.usb.Status retval, long transactionId);
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl
new file mode 100644
index 0000000..105b316
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortDataRole.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.usb;
+@VintfStability
+enum PortDataRole {
+ NONE = 0,
+ HOST = 1,
+ DEVICE = 2,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl
new file mode 100644
index 0000000..34e4334
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortMode.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.usb;
+@VintfStability
+enum PortMode {
+ NONE = 0,
+ UFP = 1,
+ DFP = 2,
+ DRP = 3,
+ AUDIO_ACCESSORY = 4,
+ DEBUG_ACCESSORY = 5,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl
new file mode 100644
index 0000000..0e6f3fb
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortPowerRole.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.usb;
+@VintfStability
+enum PortPowerRole {
+ NONE = 0,
+ SOURCE = 1,
+ SINK = 2,
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl
new file mode 100644
index 0000000..c66aecc
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortRole.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.usb;
+@VintfStability
+union PortRole {
+ android.hardware.usb.PortPowerRole powerRole = android.hardware.usb.PortPowerRole.NONE;
+ android.hardware.usb.PortDataRole dataRole;
+ android.hardware.usb.PortMode mode;
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl
new file mode 100644
index 0000000..14bb90f
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/PortStatus.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// 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.usb;
+@VintfStability
+parcelable PortStatus {
+ String portName;
+ android.hardware.usb.PortDataRole currentDataRole = android.hardware.usb.PortDataRole.NONE;
+ android.hardware.usb.PortPowerRole currentPowerRole = android.hardware.usb.PortPowerRole.NONE;
+ android.hardware.usb.PortMode currentMode = android.hardware.usb.PortMode.NONE;
+ boolean canChangeMode;
+ boolean canChangeDataRole;
+ boolean canChangePowerRole;
+ android.hardware.usb.PortMode[] supportedModes;
+ android.hardware.usb.ContaminantProtectionMode[] supportedContaminantProtectionModes;
+ boolean supportsEnableContaminantPresenceProtection;
+ android.hardware.usb.ContaminantProtectionStatus contaminantProtectionStatus = android.hardware.usb.ContaminantProtectionStatus.NONE;
+ boolean supportsEnableContaminantPresenceDetection;
+ android.hardware.usb.ContaminantDetectionStatus contaminantDetectionStatus = android.hardware.usb.ContaminantDetectionStatus.NOT_SUPPORTED;
+ boolean usbDataEnabled;
+ boolean powerTransferLimited;
+}
diff --git a/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.aidl b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.aidl
new file mode 100644
index 0000000..f28fc2a
--- /dev/null
+++ b/usb/aidl/aidl_api/android.hardware.usb/current/android/hardware/usb/Status.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.usb;
+@Backing(type="int") @VintfStability
+enum Status {
+ SUCCESS = 0,
+ ERROR = 1,
+ INVALID_ARGUMENT = 2,
+ UNRECOGNIZED_ROLE = 3,
+ NOT_SUPPORTED = 4,
+}
diff --git a/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.aidl b/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.aidl
new file mode 100644
index 0000000..d9bc576
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/ContaminantDetectionStatus.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.usb;
+
+@VintfStability
+enum ContaminantDetectionStatus {
+ /**
+ * Contaminant presence detection is not supported.
+ */
+ NOT_SUPPORTED = 0,
+ /**
+ * Contaminant presence detection is supported but disabled.
+ */
+ DISABLED = 1,
+ /**
+ * Contaminant presence detection is enabled and contaminant not detected.
+ */
+ NOT_DETECTED = 2,
+ /**
+ * Contaminant presence detection is enabled and contaminant detected.
+ */
+ DETECTED = 3,
+}
diff --git a/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl b/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl
new file mode 100644
index 0000000..47c073d
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/ContaminantProtectionMode.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.usb;
+
+@VintfStability
+enum ContaminantProtectionMode {
+ /**
+ * No action performed upon detection of contaminant presence.
+ */
+ NONE = 0,
+ /**
+ * Upon detection of contaminant presence, Port is forced to sink only
+ * mode where a port shall only detect chargers until contaminant presence
+ * is no longer detected.
+ */
+ FORCE_SINK = 1,
+ /**
+ * Upon detection of contaminant presence, Port is forced to source only
+ * mode where a port shall only detect usb accessories such as headsets
+ * until contaminant presence is no longer detected.
+ */
+ FORCE_SOURCE = 2,
+ /**
+ * Upon detection of contaminant presence, port is disabled until contaminant
+ * presence is no longer detected. In the disabled state port will
+ * not respond to connection of chargers or usb accessories.
+ */
+ FORCE_DISABLE = 3,
+}
diff --git a/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.aidl b/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.aidl
new file mode 100644
index 0000000..c4fa979
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/ContaminantProtectionStatus.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.
+ */
+
+package android.hardware.usb;
+
+import android.hardware.usb.ContaminantProtectionMode;
+
+@VintfStability
+enum ContaminantProtectionStatus {
+ /**
+ * No action performed upon detection of contaminant presence.
+ */
+ NONE = 0,
+ /**
+ * Upon detection of contaminant presence, Port is forced to sink only
+ * mode where a port shall only detect chargers until contaminant presence
+ * is no longer detected.
+ */
+ FORCE_SINK = 1,
+ /**
+ * Upon detection of contaminant presence, Port is forced to source only
+ * mode where a port shall only detect usb accessories such as headsets
+ * until contaminant presence is no longer detected.
+ */
+ FORCE_SOURCE = 2,
+ /**
+ * Upon detection of contaminant presence, port is disabled until contaminant
+ * presence is no longer detected. In the disabled state port will
+ * not respond to connection of chargers or usb accessories.
+ */
+ FORCE_DISABLE = 3,
+ /**
+ * Client disabled cotaminant protection by calling
+ * enableContaminantPresencePortProtection set to false. Low level drivers should
+ * not autmomously take any corrective action when contaminant presence is detected.
+ */
+ DISABLED = 4,
+}
diff --git a/usb/aidl/android/hardware/usb/IUsb.aidl b/usb/aidl/android/hardware/usb/IUsb.aidl
new file mode 100644
index 0000000..1596d9a
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/IUsb.aidl
@@ -0,0 +1,98 @@
+/*
+ * 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.usb;
+
+import android.hardware.usb.IUsbCallback;
+import android.hardware.usb.PortRole;
+
+@VintfStability
+oneway interface IUsb {
+ /**
+ * When supportsEnableContaminantPresenceDetection is true,
+ * enableContaminantPresenceDetection enables/disables contaminant
+ * presence detection algorithm. Calling enableContaminantPresenceDetection
+ * when supportsEnableContaminantPresenceDetection is false does
+ * not have any effect.
+ * Change in contantaminant presence status should be notified to the
+ * client via notifyPortStatusChange through PortStatus.
+ *
+ * @param portName name of the port.
+ * @param enable true Enable contaminant presence detection algorithm.
+ * false Disable contaminant presence detection algorithm.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void enableContaminantPresenceDetection(in String portName, in boolean enable, long transactionId);
+
+ /**
+ * This function is used to enable/disable USB data controller.
+ *
+ * @param portName Name of the port.
+ * @param enable true Enable USB data signaling.
+ * false Disable USB data signaling.
+ * @param transactionId ID to be used when invoking the callback.
+ *
+ */
+ void enableUsbData(in String portName, boolean enable, long transactionId);
+
+ /**
+ * This functions is used to request the hal for the current status
+ * status of the Type-C ports. The result of the query would be sent
+ * through the IUsbCallback object's notifyRoleSwitchStatus
+ * to the caller. This api would would let the caller know of the number
+ * of type-c ports that are present and their connection status through the
+ * PortStatus type.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void queryPortStatus(long transactionId);
+
+ /**
+ * This function is used to register a callback function which is
+ * called by the HAL to inform the client of port status updates and
+ * result of the requested operation. Please refer IUsbCallback for
+ * complete description of when each of the IUsbCallback's interface
+ * methods is expected to be called.
+ *
+ * @param callback IUsbCallback object used to convey status to the
+ * userspace.
+ */
+ void setCallback(in IUsbCallback callback);
+
+ /**
+ * This function is used to change the port role of a specific port.
+ * For example, when DR_SWAP or PR_SWAP is supported.
+ * The status of the role switch will be informed through IUsbCallback
+ * object's notifyPortStatusChange method.
+ *
+ * @param portName name of the port for which the role has to be changed
+ * @param role the new port role.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void switchRole(in String portName, in PortRole role, long transactionId);
+
+ /**
+ * This function is used to limit power transfer in and out of the port.
+ * When limited, the port does not charge from the partner port.
+ * Also, the port limits sourcing power to the partner port when the USB
+ * specification allows it to do so.
+ *
+ * @param portName name of the port for which power transfer is being limited.
+ * @param limit true limit power transfer.
+ * false relax limiting power transfer.
+ * @param transactionId ID to be used when invoking the callback.
+ */
+ void limitPowerTransfer(in String portName, boolean limit, long transactionId);
+}
diff --git a/usb/aidl/android/hardware/usb/IUsbCallback.aidl b/usb/aidl/android/hardware/usb/IUsbCallback.aidl
new file mode 100644
index 0000000..b733fed
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/IUsbCallback.aidl
@@ -0,0 +1,98 @@
+/*
+ * 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.usb;
+
+import android.hardware.usb.PortRole;
+import android.hardware.usb.PortStatus;
+import android.hardware.usb.Status;
+
+/**
+ * Callback object used for all the IUsb async methods which expects a result.
+ * Caller is expected to register the callback object using setCallback method
+ * to receive updates on the PortStatus.
+ */
+@VintfStability
+oneway interface IUsbCallback {
+ /**
+ * Used to convey the current port status to the caller.
+ * Must be called either when PortState changes due to the port partner or
+ * when caller requested for the PortStatus update through queryPortStatus.
+ *
+ * @param currentPortStatus describes the status of all the USB ports in the
+ * device.
+ * @param retval SUCCESS when the required information was enquired form
+ * kernel and the PortStatus object was built.
+ * ERROR otherwise.
+ */
+ void notifyPortStatusChange(in PortStatus[] currentPortStatus, in Status retval);
+
+ /**
+ * Used to notify the result of the switchRole call to the caller.
+ *
+ * @param portName name of the port for which the roleswap is requested.
+ * @param newRole the new role requested by the caller.
+ * @param retval SUCCESS if the role switch succeeded. FAILURE otherwise.
+ * @param transactionId transactionId sent during switchRole request.
+ */
+ void notifyRoleSwitchStatus(in String portName, in PortRole newRole, in Status retval,
+ long transactionId);
+
+ /**
+ * Used to notify the result of notifyEnableUsbDataStatus call to the caller.
+ *
+ * @param portName name of the port for which the enableUsbData is requested.
+ * @param enable true when usb data is enabled.
+ * false when usb data is disabled.
+ * @param retval SUCCESS if current request succeeded. FAILURE otherwise.
+ * @param transactionId transactionId sent during enableUsbData request.
+ */
+ void notifyEnableUsbDataStatus(in String portName, boolean enable, in Status retval,
+ long transactionId);
+
+ /**
+ * Used to notify the result of enableContaminantPresenceDetection.
+ *
+ * @param portName name of the port for which contaminant detection is enabled/disabled.
+ * @param enable true when contaminant detection is enabled.
+ * false when disabled.
+ * @param retval SUCCESS if the request for enabling/disabling contamiant detection succeeds.
+ * FAILURE otherwise.
+ * @param transactionId transactionId sent during queryPortStatus request
+ */
+ void notifyContaminantEnabledStatus(in String portName, boolean enable, in Status retval,
+ long transactionId);
+
+ /**
+ * Used to notify the request to query port status.
+ *
+ * @param portName name of the port for which port status is queried.
+ * @param retval SUCCESS if the port query succeeded. FAILURE otherwise.
+ * @param transactionId transactionId sent during queryPortStatus request
+ */
+ void notifyQueryPortStatus(in String portName, in Status retval, long transactionId);
+
+ /**
+ * Used to notify the result of requesting limitPowerTransfer.
+ *
+ * @param portName name of the port for which power transfer is being limited.
+ * @param limit true limit power transfer.
+ * false relax limiting power transfer.
+ * @param retval SUCCESS if the request to enable/disable limitPowerTransfer succeeds.
+ * FAILURE otherwise.
+ * @param transactionId ID sent during limitPowerTransfer request.
+ */
+ void notifyLimitPowerTransferStatus(in String portName, boolean limit, in Status retval, long transactionId);
+}
diff --git a/usb/aidl/android/hardware/usb/PortDataRole.aidl b/usb/aidl/android/hardware/usb/PortDataRole.aidl
new file mode 100644
index 0000000..a69f977
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/PortDataRole.aidl
@@ -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.
+ */
+
+package android.hardware.usb;
+
+@VintfStability
+enum PortDataRole {
+ /**
+ * Indicates that the port does not have a data role.
+ * In case of DRP, the current data role of the port is only resolved
+ * when the type-c handshake happens.
+ */
+ NONE = 0,
+ /**
+ * Indicates that the port is acting as a host for data.
+ */
+ HOST = 1,
+ /**
+ * Indicated that the port is acting as a device for data.
+ */
+ DEVICE = 2,
+}
diff --git a/usb/aidl/android/hardware/usb/PortMode.aidl b/usb/aidl/android/hardware/usb/PortMode.aidl
new file mode 100644
index 0000000..399f0eb
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/PortMode.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.usb;
+
+import android.hardware.usb.PortMode;
+
+@VintfStability
+enum PortMode {
+ /**
+ * Indicates that the port does not have a mode.
+ * In case of DRP, the current mode of the port is only resolved
+ * when the type-c handshake happens.
+ */
+ NONE = 0,
+ /**
+ * Indicates that port can only act as device for data and sink for power.
+ */
+ UFP = 1,
+ /**
+ * Indicates the port can only act as host for data and source for power.
+ */
+ DFP = 2,
+ /**
+ * Indicates can either act as UFP or DFP at a given point of time.
+ */
+ DRP = 3,
+ /*
+ * Indicates that the port supports Audio Accessory mode.
+ */
+ AUDIO_ACCESSORY = 4,
+ /*
+ * Indicates that the port supports Debug Accessory mode.
+ */
+ DEBUG_ACCESSORY = 5,
+}
diff --git a/usb/aidl/android/hardware/usb/PortPowerRole.aidl b/usb/aidl/android/hardware/usb/PortPowerRole.aidl
new file mode 100644
index 0000000..ae3dc47
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/PortPowerRole.aidl
@@ -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.
+ */
+
+package android.hardware.usb;
+
+@VintfStability
+enum PortPowerRole {
+ /**
+ * Indicates that the port does not have a power role.
+ * In case of DRP, the current power role of the port is only resolved
+ * when the type-c handshake happens.
+ */
+ NONE = 0,
+ /**
+ * Indicates that the port is supplying power to the other port.
+ */
+ SOURCE = 1,
+ /**
+ * Indicates that the port is sinking power from the other port.
+ */
+ SINK = 2,
+}
diff --git a/usb/aidl/android/hardware/usb/PortRole.aidl b/usb/aidl/android/hardware/usb/PortRole.aidl
new file mode 100644
index 0000000..e0429c8
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/PortRole.aidl
@@ -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.
+ */
+
+package android.hardware.usb;
+
+import android.hardware.usb.PortDataRole;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.PortPowerRole;
+
+/**
+ * Used as a container to send port role information.
+ */
+@VintfStability
+union PortRole {
+ PortPowerRole powerRole = PortPowerRole.NONE;
+ PortDataRole dataRole;
+ PortMode mode;
+}
diff --git a/usb/aidl/android/hardware/usb/PortStatus.aidl b/usb/aidl/android/hardware/usb/PortStatus.aidl
new file mode 100644
index 0000000..fb979e5
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/PortStatus.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.usb;
+
+import android.hardware.usb.ContaminantDetectionStatus;
+import android.hardware.usb.ContaminantProtectionMode;
+import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.PortDataRole;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.PortPowerRole;
+
+@VintfStability
+parcelable PortStatus {
+ /**
+ * Name of the port.
+ * Used as the port's id by the caller.
+ */
+ String portName;
+ /**
+ * Data role of the port.
+ */
+ PortDataRole currentDataRole = PortDataRole.NONE;
+ /**
+ * Power Role of thte port.
+ */
+ PortPowerRole currentPowerRole = PortPowerRole.NONE;
+ /**
+ * Mode in which the port is connected.
+ * Can be UFP or DFP or AUDIO_ACCESSORY or
+ * DEBUG_ACCESSORY.
+ */
+ PortMode currentMode = PortMode.NONE;
+ /**
+ * True indicates that the port's mode can
+ * be changed. False otherwise.
+ */
+ boolean canChangeMode;
+ /**
+ * True indicates that the port's data role
+ * can be changed. False otherwise.
+ * For example, true if Type-C PD PD_SWAP
+ * is supported.
+ */
+ boolean canChangeDataRole;
+ /**
+ * True indicates that the port's power role
+ * can be changed. False otherwise.
+ * For example, true if Type-C PD PR_SWAP
+ * is supported.
+ */
+ boolean canChangePowerRole;
+ /**
+ * Identifies the type of the local port.
+ *
+ * UFP - Indicates that port can only act as device for
+ * data and sink for power.
+ * DFP - Indicates the port can only act as host for data
+ * and source for power.
+ * DRP - Indicates can either act as UFP or DFP at a
+ * given point of time.
+ * AUDIO_ACCESSORY - Indicates that the port supports
+ * Audio Accessory mode.
+ * DEBUG_ACCESSORY - Indicates that the port supports
+ * Debug Accessory mode.
+ */
+ PortMode[] supportedModes;
+ /**
+ * Contaminant presence protection modes supported by the port.
+ */
+ ContaminantProtectionMode[] supportedContaminantProtectionModes;
+ /**
+ * Client can enable/disable contaminant presence protection through
+ * enableContaminantPresenceProtection when true.
+ */
+ boolean supportsEnableContaminantPresenceProtection;
+ /**
+ * Contaminant presence protection modes currently active for the port.
+ */
+ ContaminantProtectionStatus contaminantProtectionStatus = ContaminantProtectionStatus.NONE;
+ /**
+ * Client can enable/disable contaminant presence detection through
+ * enableContaminantPresenceDetection when true.
+ */
+ boolean supportsEnableContaminantPresenceDetection;
+ /**
+ * Current status of contaminant detection algorithm.
+ */
+ ContaminantDetectionStatus contaminantDetectionStatus = ContaminantDetectionStatus.NOT_SUPPORTED;
+ /**
+ * UsbData status of the port.
+ */
+ boolean usbDataEnabled;
+ /**
+ * Denoted whether power transfer is limited in the port.
+ */
+ boolean powerTransferLimited;
+}
diff --git a/usb/aidl/android/hardware/usb/Status.aidl b/usb/aidl/android/hardware/usb/Status.aidl
new file mode 100644
index 0000000..468ba4a
--- /dev/null
+++ b/usb/aidl/android/hardware/usb/Status.aidl
@@ -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.
+ */
+
+package android.hardware.usb;
+
+@VintfStability
+@Backing(type="int")
+enum Status {
+ SUCCESS = 0,
+ /**
+ * error value when the HAL operation fails for reasons not listed here.
+ */
+ ERROR = 1,
+ /**
+ * error value returned when input argument is invalid.
+ */
+ INVALID_ARGUMENT = 2,
+ /**
+ * error value returned when role string is unrecognized.
+ */
+ UNRECOGNIZED_ROLE = 3,
+ /**
+ * Error value returned when the operation is not supported.
+ */
+ NOT_SUPPORTED = 4,
+}
diff --git a/usb/aidl/conversion.log b/usb/aidl/conversion.log
new file mode 100644
index 0000000..c090446
--- /dev/null
+++ b/usb/aidl/conversion.log
@@ -0,0 +1,11 @@
+Notes relating to hidl2aidl conversion of android.hardware.usb@1.3 to android.hardware.usb (if any) follow:
+Unhandled comments from android.hardware.usb@1.1::types follow. Consider using hidl-lint to locate these and fixup as many as possible.
+ // NOTE: suffix '_1_1' is for legacy ABI compatibility. It cannot be
+ // changed to 'PortMode' which the convention dictates.
+ // NOTE: suffix '_1_1' is for legacy ABI compatibility. It cannot be
+ // changed to 'PortStatus' which the convention dictates.
+
+An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus
+An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus
+An unknown named type was found in translation: android.hardware.usb@1.0::PortStatus
+END OF LOG
diff --git a/usb/aidl/default/Android.bp b/usb/aidl/default/Android.bp
new file mode 100644
index 0000000..da0cff2
--- /dev/null
+++ b/usb/aidl/default/Android.bp
@@ -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.
+//
+
+cc_binary {
+ name: "android.hardware.usb-service.example",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.usb-service.example.rc"],
+ vintf_fragments: ["android.hardware.usb-service.example.xml"],
+ vendor: true,
+ srcs: [
+ "service.cpp",
+ "Usb.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.usb-V1-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/usb/aidl/default/Usb.cpp b/usb/aidl/default/Usb.cpp
new file mode 100644
index 0000000..0624963
--- /dev/null
+++ b/usb/aidl/default/Usb.cpp
@@ -0,0 +1,711 @@
+/*
+ * 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.usb.aidl-service"
+
+#include <aidl/android/hardware/usb/PortRole.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <assert.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <chrono>
+#include <regex>
+#include <thread>
+#include <unordered_map>
+
+#include <cutils/uevent.h>
+#include <sys/epoll.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "Usb.h"
+
+using android::base::GetProperty;
+using android::base::Trim;
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+
+constexpr char kTypecPath[] = "/sys/class/typec/";
+constexpr char kDataRoleNode[] = "/data_role";
+constexpr char kPowerRoleNode[] = "/power_role";
+
+// Set by the signal handler to destroy the thread
+volatile bool destroyThread;
+
+void queryVersionHelper(android::hardware::usb::Usb *usb,
+ std::vector<PortStatus> *currentPortStatus);
+
+ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, int64_t in_transactionId) {
+ std::vector<PortStatus> currentPortStatus;
+
+ pthread_mutex_lock(&mLock);
+ if (mCallback != NULL) {
+ ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus(
+ in_portName, true, in_enable ? Status::SUCCESS : Status::ERROR, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str());
+ } else {
+ ALOGE("Not notifying the userspace. Callback is not set");
+ }
+ pthread_mutex_unlock(&mLock);
+ queryVersionHelper(this, ¤tPortStatus);
+
+ return ScopedAStatus::ok();
+}
+
+Status queryMoistureDetectionStatus(std::vector<PortStatus> *currentPortStatus) {
+ string enabled, status, path, DetectedPath;
+
+ for (int i = 0; i < currentPortStatus->size(); i++) {
+ (*currentPortStatus)[i].supportedContaminantProtectionModes
+ .push_back(ContaminantProtectionMode::NONE);
+ (*currentPortStatus)[i].contaminantProtectionStatus
+ = ContaminantProtectionStatus::NONE;
+ (*currentPortStatus)[i].contaminantDetectionStatus
+ = ContaminantDetectionStatus::NOT_SUPPORTED;
+ (*currentPortStatus)[i].supportsEnableContaminantPresenceDetection = false;
+ (*currentPortStatus)[i].supportsEnableContaminantPresenceProtection = false;
+ }
+
+ return Status::SUCCESS;
+}
+
+string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) {
+ string node(kTypecPath + portName);
+
+ switch (tag) {
+ case PortRole::dataRole:
+ return node + kDataRoleNode;
+ case PortRole::powerRole:
+ return node + kPowerRoleNode;
+ case PortRole::mode:
+ return node + "/port_type";
+ default:
+ return "";
+ }
+}
+
+string convertRoletoString(PortRole role) {
+ if (role.getTag() == PortRole::powerRole) {
+ if (role.get<PortRole::powerRole>() == PortPowerRole::SOURCE)
+ return "source";
+ else if (role.get<PortRole::powerRole>() == PortPowerRole::SINK)
+ return "sink";
+ } else if (role.getTag() == PortRole::dataRole) {
+ if (role.get<PortRole::dataRole>() == PortDataRole::HOST)
+ return "host";
+ if (role.get<PortRole::dataRole>() == PortDataRole::DEVICE)
+ return "device";
+ } else if (role.getTag() == PortRole::mode) {
+ if (role.get<PortRole::mode>() == PortMode::UFP)
+ return "sink";
+ if (role.get<PortRole::mode>() == PortMode::DFP)
+ return "source";
+ }
+ return "none";
+}
+
+void extractRole(string *roleName) {
+ std::size_t first, last;
+
+ first = roleName->find("[");
+ last = roleName->find("]");
+
+ if (first != string::npos && last != string::npos) {
+ *roleName = roleName->substr(first + 1, last - first - 1);
+ }
+}
+
+void switchToDrp(const string &portName) {
+ string filename = appendRoleNodeHelper(string(portName.c_str()), PortRole::mode);
+ FILE *fp;
+
+ if (filename != "") {
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ int ret = fputs("dual", fp);
+ fclose(fp);
+ if (ret == EOF)
+ ALOGE("Fatal: Error while switching back to drp");
+ } else {
+ ALOGE("Fatal: Cannot open file to switch back to drp");
+ }
+ } else {
+ ALOGE("Fatal: invalid node type");
+ }
+}
+
+bool switchMode(const string &portName, const PortRole &in_role, struct Usb *usb) {
+ string filename = appendRoleNodeHelper(string(portName.c_str()), in_role.getTag());
+ string written;
+ FILE *fp;
+ bool roleSwitch = false;
+
+ if (filename == "") {
+ ALOGE("Fatal: invalid node type");
+ return false;
+ }
+
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ // Hold the lock here to prevent loosing connected signals
+ // as once the file is written the partner added signal
+ // can arrive anytime.
+ pthread_mutex_lock(&usb->mPartnerLock);
+ usb->mPartnerUp = false;
+ int ret = fputs(convertRoletoString(in_role).c_str(), fp);
+ fclose(fp);
+
+ if (ret != EOF) {
+ struct timespec to;
+ struct timespec now;
+
+ wait_again:
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT;
+ to.tv_nsec = now.tv_nsec;
+
+ int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to);
+ // There are no uevent signals which implies role swap timed out.
+ if (err == ETIMEDOUT) {
+ ALOGI("uevents wait timedout");
+ // Validity check.
+ } else if (!usb->mPartnerUp) {
+ goto wait_again;
+ // Role switch succeeded since usb->mPartnerUp is true.
+ } else {
+ roleSwitch = true;
+ }
+ } else {
+ ALOGI("Role switch failed while wrting to file");
+ }
+ pthread_mutex_unlock(&usb->mPartnerLock);
+ }
+
+ if (!roleSwitch)
+ switchToDrp(string(portName.c_str()));
+
+ return roleSwitch;
+}
+
+Usb::Usb()
+ : mLock(PTHREAD_MUTEX_INITIALIZER),
+ mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER),
+ mPartnerLock(PTHREAD_MUTEX_INITIALIZER),
+ mPartnerUp(false)
+{
+ pthread_condattr_t attr;
+ if (pthread_condattr_init(&attr)) {
+ ALOGE("pthread_condattr_init failed: %s", strerror(errno));
+ abort();
+ }
+ if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) {
+ ALOGE("pthread_condattr_setclock failed: %s", strerror(errno));
+ abort();
+ }
+ if (pthread_cond_init(&mPartnerCV, &attr)) {
+ ALOGE("pthread_cond_init failed: %s", strerror(errno));
+ abort();
+ }
+ if (pthread_condattr_destroy(&attr)) {
+ ALOGE("pthread_condattr_destroy failed: %s", strerror(errno));
+ abort();
+ }
+}
+
+ScopedAStatus Usb::switchRole(const string& in_portName,
+ const PortRole& in_role, int64_t in_transactionId) {
+ string filename = appendRoleNodeHelper(string(in_portName.c_str()), in_role.getTag());
+ string written;
+ FILE *fp;
+ bool roleSwitch = false;
+
+ if (filename == "") {
+ ALOGE("Fatal: invalid node type");
+ return ScopedAStatus::ok();
+ }
+
+ pthread_mutex_lock(&mRoleSwitchLock);
+
+ ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(in_role).c_str());
+
+ if (in_role.getTag() == PortRole::mode) {
+ roleSwitch = switchMode(in_portName, in_role, this);
+ } else {
+ fp = fopen(filename.c_str(), "w");
+ if (fp != NULL) {
+ int ret = fputs(convertRoletoString(in_role).c_str(), fp);
+ fclose(fp);
+ if ((ret != EOF) && ReadFileToString(filename, &written)) {
+ written = Trim(written);
+ extractRole(&written);
+ ALOGI("written: %s", written.c_str());
+ if (written == convertRoletoString(in_role)) {
+ roleSwitch = true;
+ } else {
+ ALOGE("Role switch failed");
+ }
+ } else {
+ ALOGE("failed to update the new role");
+ }
+ } else {
+ ALOGE("fopen failed");
+ }
+ }
+
+ pthread_mutex_lock(&mLock);
+ if (mCallback != NULL) {
+ ScopedAStatus ret = mCallback->notifyRoleSwitchStatus(
+ in_portName, in_role, roleSwitch ? Status::SUCCESS : Status::ERROR, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("RoleSwitchStatus error %s", ret.getDescription().c_str());
+ } else {
+ ALOGE("Not notifying the userspace. Callback is not set");
+ }
+ pthread_mutex_unlock(&mLock);
+ pthread_mutex_unlock(&mRoleSwitchLock);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool /*in_limit*/,
+ int64_t in_transactionId) {
+ std::vector<PortStatus> currentPortStatus;
+
+ pthread_mutex_lock(&mLock);
+ if (mCallback != NULL && in_transactionId >= 0) {
+ ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus(
+ in_portName, false, Status::NOT_SUPPORTED, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("limitPowerTransfer error %s", ret.getDescription().c_str());
+ } else {
+ ALOGE("Not notifying the userspace. Callback is not set");
+ }
+ pthread_mutex_unlock(&mLock);
+
+ return ScopedAStatus::ok();
+}
+
+Status getAccessoryConnected(const string &portName, string *accessory) {
+ string filename = kTypecPath + portName + "-partner/accessory_mode";
+
+ if (!ReadFileToString(filename, accessory)) {
+ ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", filename.c_str());
+ return Status::ERROR;
+ }
+ *accessory = Trim(*accessory);
+
+ return Status::SUCCESS;
+}
+
+Status getCurrentRoleHelper(const string &portName, bool connected, PortRole *currentRole) {
+ string filename;
+ string roleName;
+ string accessory;
+
+ // Mode
+
+ if (currentRole->getTag() == PortRole::powerRole) {
+ filename = kTypecPath + portName + kPowerRoleNode;
+ currentRole->set<PortRole::powerRole>(PortPowerRole::NONE);
+ } else if (currentRole->getTag() == PortRole::dataRole) {
+ filename = kTypecPath + portName + kDataRoleNode;
+ currentRole->set<PortRole::dataRole>(PortDataRole::NONE);
+ } else if (currentRole->getTag() == PortRole::mode) {
+ filename = kTypecPath + portName + kDataRoleNode;
+ currentRole->set<PortRole::mode>(PortMode::NONE);
+ } else {
+ return Status::ERROR;
+ }
+
+ if (!connected)
+ return Status::SUCCESS;
+
+ if (currentRole->getTag() == PortRole::mode) {
+ if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) {
+ return Status::ERROR;
+ }
+ if (accessory == "analog_audio") {
+ currentRole->set<PortRole::mode>(PortMode::AUDIO_ACCESSORY);
+ return Status::SUCCESS;
+ } else if (accessory == "debug") {
+ currentRole->set<PortRole::mode>(PortMode::DEBUG_ACCESSORY);
+ return Status::SUCCESS;
+ }
+ }
+
+ if (!ReadFileToString(filename, &roleName)) {
+ ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str());
+ return Status::ERROR;
+ }
+
+ roleName = Trim(roleName);
+ extractRole(&roleName);
+
+ if (roleName == "source") {
+ currentRole->set<PortRole::powerRole>(PortPowerRole::SOURCE);
+ } else if (roleName == "sink") {
+ currentRole->set<PortRole::powerRole>(PortPowerRole::SINK);
+ } else if (roleName == "host") {
+ if (currentRole->getTag() == PortRole::dataRole)
+ currentRole->set<PortRole::dataRole>(PortDataRole::HOST);
+ else
+ currentRole->set<PortRole::mode>(PortMode::DFP);
+ } else if (roleName == "device") {
+ if (currentRole->getTag() == PortRole::dataRole)
+ currentRole->set<PortRole::dataRole>(PortDataRole::DEVICE);
+ else
+ currentRole->set<PortRole::mode>(PortMode::UFP);
+ } else if (roleName != "none") {
+ /* case for none has already been addressed.
+ * so we check if the role isn't none.
+ */
+ return Status::UNRECOGNIZED_ROLE;
+ }
+
+ return Status::SUCCESS;
+}
+
+Status getTypeCPortNamesHelper(std::unordered_map<string, bool> *names) {
+ DIR *dp;
+
+ dp = opendir(kTypecPath);
+ if (dp != NULL) {
+ struct dirent *ep;
+
+ while ((ep = readdir(dp))) {
+ if (ep->d_type == DT_LNK) {
+ if (string::npos == string(ep->d_name).find("-partner")) {
+ std::unordered_map<string, bool>::const_iterator portName =
+ names->find(ep->d_name);
+ if (portName == names->end()) {
+ names->insert({ep->d_name, false});
+ }
+ } else {
+ (*names)[std::strtok(ep->d_name, "-")] = true;
+ }
+ }
+ }
+ closedir(dp);
+ return Status::SUCCESS;
+ }
+
+ ALOGE("Failed to open /sys/class/typec");
+ return Status::ERROR;
+}
+
+bool canSwitchRoleHelper(const string &portName) {
+ string filename = kTypecPath + portName + "-partner/supports_usb_power_delivery";
+ string supportsPD;
+
+ if (ReadFileToString(filename, &supportsPD)) {
+ supportsPD = Trim(supportsPD);
+ if (supportsPD == "yes") {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Status getPortStatusHelper(std::vector<PortStatus> *currentPortStatus) {
+ std::unordered_map<string, bool> names;
+ Status result = getTypeCPortNamesHelper(&names);
+ int i = -1;
+
+ if (result == Status::SUCCESS) {
+ currentPortStatus->resize(names.size());
+ for (std::pair<string, bool> port : names) {
+ i++;
+ ALOGI("%s", port.first.c_str());
+ (*currentPortStatus)[i].portName = port.first;
+
+ PortRole currentRole;
+ currentRole.set<PortRole::powerRole>(PortPowerRole::NONE);
+ if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS){
+ (*currentPortStatus)[i].currentPowerRole = currentRole.get<PortRole::powerRole>();
+ } else {
+ ALOGE("Error while retrieving portNames");
+ goto done;
+ }
+
+ currentRole.set<PortRole::dataRole>(PortDataRole::NONE);
+ if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) {
+ (*currentPortStatus)[i].currentDataRole = currentRole.get<PortRole::dataRole>();
+ } else {
+ ALOGE("Error while retrieving current port role");
+ goto done;
+ }
+
+ currentRole.set<PortRole::mode>(PortMode::NONE);
+ if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) {
+ (*currentPortStatus)[i].currentMode = currentRole.get<PortRole::mode>();
+ } else {
+ ALOGE("Error while retrieving current data role");
+ goto done;
+ }
+
+ (*currentPortStatus)[i].canChangeMode = true;
+ (*currentPortStatus)[i].canChangeDataRole =
+ port.second ? canSwitchRoleHelper(port.first) : false;
+ (*currentPortStatus)[i].canChangePowerRole =
+ port.second ? canSwitchRoleHelper(port.first) : false;
+
+ (*currentPortStatus)[i].supportedModes.push_back(PortMode::DRP);
+ (*currentPortStatus)[i].usbDataEnabled = true;
+
+ ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d "
+ "usbDataEnabled:%d",
+ i, port.first.c_str(), port.second,
+ (*currentPortStatus)[i].canChangeMode,
+ (*currentPortStatus)[i].canChangeDataRole,
+ (*currentPortStatus)[i].canChangePowerRole, 0);
+ }
+
+ return Status::SUCCESS;
+ }
+done:
+ return Status::ERROR;
+}
+
+void queryVersionHelper(android::hardware::usb::Usb *usb,
+ std::vector<PortStatus> *currentPortStatus) {
+ Status status;
+ pthread_mutex_lock(&usb->mLock);
+ status = getPortStatusHelper(currentPortStatus);
+ queryMoistureDetectionStatus(currentPortStatus);
+ if (usb->mCallback != NULL) {
+ ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus,
+ status);
+ if (!ret.isOk())
+ ALOGE("queryPortStatus error %s", ret.getDescription().c_str());
+ } else {
+ ALOGI("Notifying userspace skipped. Callback is NULL");
+ }
+ pthread_mutex_unlock(&usb->mLock);
+}
+
+ScopedAStatus Usb::queryPortStatus(int64_t in_transactionId) {
+ std::vector<PortStatus> currentPortStatus;
+
+ queryVersionHelper(this, ¤tPortStatus);
+ pthread_mutex_lock(&mLock);
+ if (mCallback != NULL) {
+ ScopedAStatus ret = mCallback->notifyQueryPortStatus(
+ "all", Status::SUCCESS, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("notifyQueryPortStatus error %s", ret.getDescription().c_str());
+ } else {
+ ALOGE("Not notifying the userspace. Callback is not set");
+ }
+ pthread_mutex_unlock(&mLock);
+
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus Usb::enableContaminantPresenceDetection(const string& in_portName,
+ bool /*in_enable*/, int64_t in_transactionId) {
+ std::vector<PortStatus> currentPortStatus;
+
+ pthread_mutex_lock(&mLock);
+ if (mCallback != NULL) {
+ ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus(
+ in_portName, false, Status::ERROR, in_transactionId);
+ if (!ret.isOk())
+ ALOGE("enableContaminantPresenceDetection error %s", ret.getDescription().c_str());
+ } else {
+ ALOGE("Not notifying the userspace. Callback is not set");
+ }
+ pthread_mutex_unlock(&mLock);
+
+ queryVersionHelper(this, ¤tPortStatus);
+ return ScopedAStatus::ok();
+}
+
+
+struct data {
+ int uevent_fd;
+ ::aidl::android::hardware::usb::Usb *usb;
+};
+
+static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
+ char msg[UEVENT_MSG_LEN + 2];
+ char *cp;
+ int n;
+
+ n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN);
+ if (n <= 0)
+ return;
+ if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
+ return;
+
+ msg[n] = '\0';
+ msg[n + 1] = '\0';
+ cp = msg;
+
+ while (*cp) {
+ if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) {
+ ALOGI("partner added");
+ pthread_mutex_lock(&payload->usb->mPartnerLock);
+ payload->usb->mPartnerUp = true;
+ pthread_cond_signal(&payload->usb->mPartnerCV);
+ pthread_mutex_unlock(&payload->usb->mPartnerLock);
+ } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) {
+ std::vector<PortStatus> currentPortStatus;
+ queryVersionHelper(payload->usb, ¤tPortStatus);
+
+ // Role switch is not in progress and port is in disconnected state
+ if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) {
+ for (unsigned long i = 0; i < currentPortStatus.size(); i++) {
+ DIR *dp =
+ opendir(string(kTypecPath +
+ string(currentPortStatus[i].portName.c_str()) +
+ "-partner").c_str());
+ if (dp == NULL) {
+ switchToDrp(currentPortStatus[i].portName);
+ } else {
+ closedir(dp);
+ }
+ }
+ pthread_mutex_unlock(&payload->usb->mRoleSwitchLock);
+ }
+ break;
+ } /* advance to after the next \0 */
+ while (*cp++) {
+ }
+ }
+}
+
+void *work(void *param) {
+ int epoll_fd, uevent_fd;
+ struct epoll_event ev;
+ int nevents = 0;
+ struct data payload;
+
+ uevent_fd = uevent_open_socket(UEVENT_MAX_EVENTS * UEVENT_MSG_LEN, true);
+
+ if (uevent_fd < 0) {
+ ALOGE("uevent_init: uevent_open_socket failed\n");
+ return NULL;
+ }
+
+ payload.uevent_fd = uevent_fd;
+ payload.usb = (::aidl::android::hardware::usb::Usb *)param;
+
+ fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
+
+ ev.events = EPOLLIN;
+ ev.data.ptr = (void *)uevent_event;
+
+ epoll_fd = epoll_create(UEVENT_MAX_EVENTS);
+ if (epoll_fd == -1) {
+ ALOGE("epoll_create failed; errno=%d", errno);
+ goto error;
+ }
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
+ ALOGE("epoll_ctl failed; errno=%d", errno);
+ goto error;
+ }
+
+ while (!destroyThread) {
+ struct epoll_event events[UEVENT_MAX_EVENTS];
+
+ nevents = epoll_wait(epoll_fd, events, UEVENT_MAX_EVENTS, -1);
+ if (nevents == -1) {
+ if (errno == EINTR)
+ continue;
+ ALOGE("usb epoll_wait failed; errno=%d", errno);
+ break;
+ }
+
+ for (int n = 0; n < nevents; ++n) {
+ if (events[n].data.ptr)
+ (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events,
+ &payload);
+ }
+ }
+
+ ALOGI("exiting worker thread");
+error:
+ close(uevent_fd);
+
+ if (epoll_fd >= 0)
+ close(epoll_fd);
+
+ return NULL;
+}
+
+void sighandler(int sig) {
+ if (sig == SIGUSR1) {
+ destroyThread = true;
+ ALOGI("destroy set");
+ return;
+ }
+ signal(SIGUSR1, sighandler);
+}
+
+ScopedAStatus Usb::setCallback(
+ const shared_ptr<IUsbCallback>& in_callback) {
+
+ pthread_mutex_lock(&mLock);
+ if ((mCallback == NULL && in_callback == NULL) ||
+ (mCallback != NULL && in_callback != NULL)) {
+ mCallback = in_callback;
+ pthread_mutex_unlock(&mLock);
+ return ScopedAStatus::ok();
+ }
+
+ mCallback = in_callback;
+ ALOGI("registering callback");
+
+ if (mCallback == NULL) {
+ if (!pthread_kill(mPoll, SIGUSR1)) {
+ pthread_join(mPoll, NULL);
+ ALOGI("pthread destroyed");
+ }
+ pthread_mutex_unlock(&mLock);
+ return ScopedAStatus::ok();
+ }
+
+ destroyThread = false;
+ signal(SIGUSR1, sighandler);
+
+ /*
+ * Create a background thread if the old callback value is NULL
+ * and being updated with a new value.
+ */
+ if (pthread_create(&mPoll, NULL, work, this)) {
+ ALOGE("pthread creation failed %d", errno);
+ mCallback = NULL;
+ }
+
+ pthread_mutex_unlock(&mLock);
+ return ScopedAStatus::ok();
+}
+
+} // namespace usb
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/usb/aidl/default/Usb.h b/usb/aidl/default/Usb.h
new file mode 100644
index 0000000..71ec938
--- /dev/null
+++ b/usb/aidl/default/Usb.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/file.h>
+#include <aidl/android/hardware/usb/BnUsb.h>
+#include <aidl/android/hardware/usb/BnUsbCallback.h>
+#include <utils/Log.h>
+
+#define UEVENT_MSG_LEN 2048
+#define UEVENT_MAX_EVENTS 64
+// The type-c stack waits for 4.5 - 5.5 secs before declaring a port non-pd.
+// The -partner directory would not be created until this is done.
+// Having a margin of ~3 secs for the directory and other related bookeeping
+// structures created and uvent fired.
+#define PORT_TYPE_TIMEOUT 8
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace usb {
+
+using ::aidl::android::hardware::usb::IUsbCallback;
+using ::aidl::android::hardware::usb::PortRole;
+using ::android::base::ReadFileToString;
+using ::android::base::WriteStringToFile;
+using ::android::sp;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+using ::std::string;
+
+struct Usb : public BnUsb {
+ Usb();
+
+ ScopedAStatus enableContaminantPresenceDetection(const std::string& in_portName,
+ bool in_enable, int64_t in_transactionId) override;
+ ScopedAStatus queryPortStatus(int64_t in_transactionId) override;
+ ScopedAStatus setCallback(const shared_ptr<IUsbCallback>& in_callback) override;
+ ScopedAStatus switchRole(const string& in_portName, const PortRole& in_role,
+ int64_t in_transactionId) override;
+ ScopedAStatus enableUsbData(const string& in_portName, bool in_enable,
+ int64_t in_transactionId) override;
+ ScopedAStatus limitPowerTransfer(const std::string& in_portName, bool in_limit,
+ int64_t in_transactionId)override;
+
+ shared_ptr<IUsbCallback> mCallback;
+ // Protects mCallback variable
+ pthread_mutex_t mLock;
+ // Protects roleSwitch operation
+ pthread_mutex_t mRoleSwitchLock;
+ // Threads waiting for the partner to come back wait here
+ pthread_cond_t mPartnerCV;
+ // lock protecting mPartnerCV
+ pthread_mutex_t mPartnerLock;
+ // Variable to signal partner coming back online after type switch
+ bool mPartnerUp;
+ private:
+ pthread_t mPoll;
+};
+
+} // namespace usb
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/usb/aidl/default/android.hardware.usb-service.example.rc b/usb/aidl/default/android.hardware.usb-service.example.rc
new file mode 100644
index 0000000..335bca7
--- /dev/null
+++ b/usb/aidl/default/android.hardware.usb-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.usb_default /vendor/bin/hw/android.hardware.usb-service.example
+ class hal
+ user system
+ group system
diff --git a/usb/aidl/default/android.hardware.usb-service.example.xml b/usb/aidl/default/android.hardware.usb-service.example.xml
new file mode 100644
index 0000000..6088194
--- /dev/null
+++ b/usb/aidl/default/android.hardware.usb-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.usb</name>
+ <version>1</version>
+ <interface>
+ <name>IUsb</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/usb/aidl/default/service.cpp b/usb/aidl/default/service.cpp
new file mode 100644
index 0000000..398458a
--- /dev/null
+++ b/usb/aidl/default/service.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_manager.h>
+#include <android/binder_process.h>
+
+#include "Usb.h"
+
+using ::aidl::android::hardware::usb::Usb;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<Usb> usb = ndk::SharedRefBase::make<Usb>();
+
+ const std::string instance = std::string() + Usb::descriptor + "/default";
+ binder_status_t status = AServiceManager_addService(usb->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return -1; // Should never be reached
+}
diff --git a/usb/aidl/vts/Android.bp b/usb/aidl/vts/Android.bp
new file mode 100644
index 0000000..00a7c93
--- /dev/null
+++ b/usb/aidl/vts/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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: "VtsAidlUsbTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsAidlUsbTargetTest.cpp"],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
+ static_libs: [
+ "android.hardware.usb-V1-ndk",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+}
diff --git a/usb/aidl/vts/VtsAidlUsbTargetTest.cpp b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
new file mode 100644
index 0000000..bab5a3a
--- /dev/null
+++ b/usb/aidl/vts/VtsAidlUsbTargetTest.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Probject
+ *
+ * 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 "UsbAidlTest"
+#include <android-base/logging.h>
+
+#include <aidl/android/hardware/usb/IUsb.h>
+#include <aidl/android/hardware/usb/IUsbCallback.h>
+#include <aidl/android/hardware/usb/BnUsbCallback.h>
+#include <aidl/android/hardware/usb/PortDataRole.h>
+#include <aidl/android/hardware/usb/PortMode.h>
+#include <aidl/android/hardware/usb/PortPowerRole.h>
+#include <aidl/android/hardware/usb/PortRole.h>
+#include <aidl/android/hardware/usb/PortStatus.h>
+#include <aidl/android/hardware/usb/Status.h>
+#include <aidl/Vintf.h>
+#include <aidl/Gtest.h>
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <stdlib.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#define TIMEOUT_PERIOD 10
+
+using ::aidl::android::hardware::usb::BnUsbCallback;
+using ::aidl::android::hardware::usb::IUsb;
+using ::aidl::android::hardware::usb::IUsbCallback;
+using ::aidl::android::hardware::usb::PortDataRole;
+using ::aidl::android::hardware::usb::PortMode;
+using ::aidl::android::hardware::usb::PortPowerRole;
+using ::aidl::android::hardware::usb::PortRole;
+using ::aidl::android::hardware::usb::PortStatus;
+using ::aidl::android::hardware::usb::Status;
+
+using ::ndk::ScopedAStatus;
+using ::ndk::SpAIBinder;
+using std::vector;
+using std::shared_ptr;
+using std::string;
+
+// The main test class for the USB aidl hal
+class UsbAidlTest : public testing::TestWithParam<std::string> {
+ public:
+ // Callback class for the USB aidl hal.
+ // Usb Hal will call this object upon role switch or port query.
+ class UsbCallback : public BnUsbCallback {
+ UsbAidlTest& parent_;
+ int cookie;
+
+ public:
+ UsbCallback(UsbAidlTest& parent, int cookie)
+ : parent_(parent), cookie(cookie){};
+
+ virtual ~UsbCallback() = default;
+
+ // Callback method for the port status.
+ ScopedAStatus notifyPortStatusChange(const vector<PortStatus>& currentPortStatus,
+ Status retval) override {
+ if (retval == Status::SUCCESS && currentPortStatus.size() > 0) {
+ parent_.usb_last_port_status.portName =
+ currentPortStatus[0].portName.c_str();
+ parent_.usb_last_port_status.currentDataRole =
+ currentPortStatus[0].currentDataRole;
+ parent_.usb_last_port_status.currentPowerRole =
+ currentPortStatus[0].currentPowerRole;
+ parent_.usb_last_port_status.currentMode =
+ currentPortStatus[0].currentMode;
+ }
+ parent_.usb_last_cookie = cookie;
+ return ScopedAStatus::ok();
+ }
+
+ // Callback method for the status of role switch operation.
+ ScopedAStatus notifyRoleSwitchStatus(const string& /*portName*/, const PortRole& newRole,
+ Status retval, int64_t transactionId) override {
+ parent_.usb_last_status = retval;
+ parent_.usb_last_cookie = cookie;
+ parent_.usb_last_port_role = newRole;
+ parent_.usb_role_switch_done = true;
+ parent_.last_transactionId = transactionId;
+ parent_.notify();
+ return ScopedAStatus::ok();
+ }
+
+ // Callback method for the status of enableUsbData operation
+ ScopedAStatus notifyEnableUsbDataStatus(const string& /*portName*/, bool /*enable*/,
+ Status /*retval*/, int64_t transactionId) override {
+ parent_.last_transactionId = transactionId;
+ parent_.usb_last_cookie = cookie;
+ parent_.enable_usb_data_done = true;
+ parent_.notify();
+ return ScopedAStatus::ok();
+ }
+
+ // Callback method for the status of enableContaminantPresenceDetection
+ ScopedAStatus notifyContaminantEnabledStatus(const string& /*portName*/, bool /*enable*/,
+ Status /*retval*/, int64_t transactionId) override {
+ parent_.last_transactionId = transactionId;
+ parent_.usb_last_cookie = cookie;
+ parent_.enable_contaminant_done = true;
+ parent_.notify();
+ return ScopedAStatus::ok();
+ }
+
+ // Callback method for the status of queryPortStatus operation
+ ScopedAStatus notifyQueryPortStatus(const string& /*portName*/, Status /*retval*/,
+ int64_t transactionId) override {
+ parent_.last_transactionId = transactionId;
+ parent_.notify();
+ return ScopedAStatus::ok();
+ }
+
+ // Callback method for the status of limitPowerTransfer operation
+ ScopedAStatus notifyLimitPowerTransferStatus(const string& /*portName*/, bool /*limit*/,
+ Status /*retval*/, int64_t transactionId) override {
+ parent_.last_transactionId = transactionId;
+ parent_.usb_last_cookie = cookie;
+ parent_.limit_power_transfer_done = true;
+ parent_.notify();
+ return ScopedAStatus::ok();
+ }
+ };
+
+ virtual void SetUp() override {
+ ALOGI("Setup");
+ usb = IUsb::fromBinder(
+ SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+ ASSERT_NE(usb, nullptr);
+
+ usb_cb_2 = ::ndk::SharedRefBase::make<UsbCallback>(*this, 2);
+ ASSERT_NE(usb_cb_2, nullptr);
+ const auto& ret = usb->setCallback(usb_cb_2);
+ ASSERT_TRUE(ret.isOk());
+ }
+
+ virtual void TearDown() override { ALOGI("Teardown"); }
+
+ // Used as a mechanism to inform the test about data/event callback.
+ inline void notify() {
+ std::unique_lock<std::mutex> lock(usb_mtx);
+ usb_count++;
+ usb_cv.notify_one();
+ }
+
+ // Test code calls this function to wait for data/event callback.
+ inline std::cv_status wait() {
+ std::unique_lock<std::mutex> lock(usb_mtx);
+
+ std::cv_status status = std::cv_status::no_timeout;
+ auto now = std::chrono::system_clock::now();
+ while (usb_count == 0) {
+ status =
+ usb_cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ if (status == std::cv_status::timeout) {
+ ALOGI("timeout");
+ return status;
+ }
+ }
+ usb_count--;
+ return status;
+ }
+
+ // USB aidl hal Proxy
+ shared_ptr<IUsb> usb;
+
+ // Callback objects for usb aidl
+ // Methods of these objects are called to notify port status updates.
+ shared_ptr<IUsbCallback> usb_cb_1, usb_cb_2;
+
+ // The last conveyed status of the USB ports.
+ // Stores information of currentt_data_role, power_role for all the USB ports
+ PortStatus usb_last_port_status;
+
+ // Status of the last role switch operation.
+ Status usb_last_status;
+
+ // Port role information of the last role switch operation.
+ PortRole usb_last_port_role;
+
+ // Flag to indicate the invocation of role switch callback.
+ bool usb_role_switch_done;
+
+ // Flag to indicate the invocation of notifyContaminantEnabledStatus callback.
+ bool enable_contaminant_done;
+
+ // Flag to indicate the invocation of notifyEnableUsbDataStatus callback.
+ bool enable_usb_data_done;
+
+ // Flag to indicate the invocation of notifyLimitPowerTransferStatus callback.
+ bool limit_power_transfer_done;
+
+ // Stores the cookie of the last invoked usb callback object.
+ int usb_last_cookie;
+
+ // Last transaction ID that was recorded.
+ int64_t last_transactionId;
+ // synchronization primitives to coordinate between main test thread
+ // and the callback thread.
+ std::mutex usb_mtx;
+ std::condition_variable usb_cv;
+ int usb_count = 0;
+};
+
+/*
+ * Test to see if setCallback succeeds.
+ * Callback object is created and registered.
+ */
+TEST_P(UsbAidlTest, setCallback) {
+ ALOGI("UsbAidlTest setCallback start");
+ usb_cb_1 = ::ndk::SharedRefBase::make<UsbCallback>(*this, 1);
+ ASSERT_NE(usb_cb_1, nullptr);
+ const auto& ret = usb->setCallback(usb_cb_1);
+ ASSERT_TRUE(ret.isOk());
+ ALOGI("UsbAidlTest setCallback end");
+}
+
+/*
+ * Check to see if querying type-c
+ * port status succeeds.
+ * The callback parameters are checked to see if the transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, queryPortStatus) {
+ ALOGI("UsbAidlTest queryPortStatus start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ ALOGI("UsbAidlTest queryPortStatus end: %s", usb_last_port_status.portName.c_str());
+}
+
+/*
+ * Trying to switch a non-existent port should fail.
+ * This test case tried to switch the port with empty
+ * name which is expected to fail.
+ * The callback parameters are checked to see if the transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, switchEmptyPort) {
+ ALOGI("UsbAidlTest switchEmptyPort start");
+ PortRole role;
+ role.set<PortRole::powerRole>(PortPowerRole::SOURCE);
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->switchRole("", role, transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(Status::ERROR, usb_last_status);
+ EXPECT_EQ(transactionId, last_transactionId);
+ EXPECT_EQ(2, usb_last_cookie);
+ ALOGI("UsbAidlTest switchEmptyPort end");
+}
+
+/*
+ * Test switching the power role of usb port.
+ * Test case queries the usb ports present in device.
+ * If there is at least one usb port, a power role switch
+ * to SOURCE is attempted for the port.
+ * The callback parameters are checked to see if the transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, switchPowerRole) {
+ ALOGI("UsbAidlTest switchPowerRole start");
+ PortRole role;
+ role.set<PortRole::powerRole>(PortPowerRole::SOURCE);
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.portName.empty()) {
+ string portBeingSwitched = usb_last_port_status.portName;
+ ALOGI("switchPower role portname:%s", portBeingSwitched.c_str());
+ usb_role_switch_done = false;
+ transactionId = rand() % 10000;
+ const auto& ret = usb->switchRole(portBeingSwitched, role, transactionId);
+ ASSERT_TRUE(ret.isOk());
+
+ std::cv_status waitStatus = wait();
+ while (waitStatus == std::cv_status::no_timeout &&
+ usb_role_switch_done == false)
+ waitStatus = wait();
+
+ EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ }
+ ALOGI("UsbAidlTest switchPowerRole end");
+}
+
+/*
+ * Test switching the data role of usb port.
+ * Test case queries the usb ports present in device.
+ * If there is at least one usb port, a data role switch
+ * to device is attempted for the port.
+ * The callback parameters are checked to see if transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, switchDataRole) {
+ ALOGI("UsbAidlTest switchDataRole start");
+ PortRole role;
+ role.set<PortRole::dataRole>(PortDataRole::DEVICE);
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.portName.empty()) {
+ string portBeingSwitched = usb_last_port_status.portName;
+ ALOGI("portname:%s", portBeingSwitched.c_str());
+ usb_role_switch_done = false;
+ transactionId = rand() % 10000;
+ const auto& ret = usb->switchRole(portBeingSwitched, role, transactionId);
+ ASSERT_TRUE(ret.isOk());
+
+ std::cv_status waitStatus = wait();
+ while (waitStatus == std::cv_status::no_timeout &&
+ usb_role_switch_done == false)
+ waitStatus = wait();
+
+ EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ }
+ ALOGI("UsbAidlTest switchDataRole end");
+}
+
+/*
+ * Test enabling contaminant presence detection of the port.
+ * Test case queries the usb ports present in device.
+ * If there is at least one usb port, enabling contaminant detection
+ * is attempted for the port.
+ * The callback parameters are checked to see if transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, enableContaminantPresenceDetection) {
+ ALOGI("UsbAidlTest enableContaminantPresenceDetection start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.portName.empty()) {
+ ALOGI("portname:%s", usb_last_port_status.portName.c_str());
+ enable_contaminant_done = false;
+ transactionId = rand() % 10000;
+ const auto& ret = usb->enableContaminantPresenceDetection(usb_last_port_status.portName,
+ true, transactionId);
+ ASSERT_TRUE(ret.isOk());
+
+ std::cv_status waitStatus = wait();
+ while (waitStatus == std::cv_status::no_timeout &&
+ enable_contaminant_done == false)
+ waitStatus = wait();
+
+ EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ }
+ ALOGI("UsbAidlTest enableContaminantPresenceDetection end");
+}
+
+/*
+ * Test enabling Usb data of the port.
+ * Test case queries the usb ports present in device.
+ * If there is at least one usb port, enabling Usb data is attempted
+ * for the port.
+ * The callback parameters are checked to see if transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, enableUsbData) {
+ ALOGI("UsbAidlTest enableUsbData start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.portName.empty()) {
+ ALOGI("portname:%s", usb_last_port_status.portName.c_str());
+ enable_usb_data_done = false;
+ transactionId = rand() % 10000;
+ const auto& ret = usb->enableUsbData(usb_last_port_status.portName, true, transactionId);
+ ASSERT_TRUE(ret.isOk());
+
+ std::cv_status waitStatus = wait();
+ while (waitStatus == std::cv_status::no_timeout &&
+ enable_usb_data_done == false)
+ waitStatus = wait();
+
+ EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ }
+ ALOGI("UsbAidlTest enableUsbData end");
+}
+
+/*
+ * Test enabling Usb data of the port.
+ * Test case queries the usb ports present in device.
+ * If there is at least one usb port, relaxing limit power transfer
+ * is attempted for the port.
+ * The callback parameters are checked to see if transaction id
+ * matches.
+ */
+TEST_P(UsbAidlTest, limitPowerTransfer) {
+ ALOGI("UsbAidlTest limitPowerTransfer start");
+ int64_t transactionId = rand() % 10000;
+ const auto& ret = usb->queryPortStatus(transactionId);
+ ASSERT_TRUE(ret.isOk());
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+
+ if (!usb_last_port_status.portName.empty()) {
+ ALOGI("portname:%s", usb_last_port_status.portName.c_str());
+ limit_power_transfer_done = false;
+ transactionId = rand() % 10000;
+ const auto& ret = usb->limitPowerTransfer(usb_last_port_status.portName, false, transactionId);
+ ASSERT_TRUE(ret.isOk());
+
+ std::cv_status waitStatus = wait();
+ while (waitStatus == std::cv_status::no_timeout &&
+ limit_power_transfer_done == false)
+ waitStatus = wait();
+
+ EXPECT_EQ(std::cv_status::no_timeout, waitStatus);
+ EXPECT_EQ(2, usb_last_cookie);
+ EXPECT_EQ(transactionId, last_transactionId);
+ }
+ ALOGI("UsbAidlTest limitPowerTransfer end");
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, UsbAidlTest,
+ testing::ValuesIn(::android::getAidlHalInstanceNames(IUsb::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}