Merge "Fix nan Vts test"
diff --git a/camera/provider/2.6/ICameraProvider.hal b/camera/provider/2.6/ICameraProvider.hal
index 0948db6..2c79c27 100644
--- a/camera/provider/2.6/ICameraProvider.hal
+++ b/camera/provider/2.6/ICameraProvider.hal
@@ -38,20 +38,24 @@
* streaming concurrently with the other camera ids in the combination.
*
* Target 1 Target 2
- * ---------------------------------------------
- * | Type | Size | Type | Size |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | |
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | |
- * ---------------------------------------------
- * | YUV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | PRIV |1280 X 720|
- * ---------------------------------------------
- * | PRIV | 1280 X 720 | YUV |1280 X 720|
- * ---------------------------------------------
-
+ * -----------------------------------------------------
+ * | Type | Size | Type | Size |
+ * -----------------------------------------------------
+ * | YUV | s1440p | |
+ * -----------------------------------------------------
+ * | JPEG | s1440p | |
+ * -----------------------------------------------------
+ * | PRIV | s1440p | |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | YUV / PRIV | s1440p |
+ * -----------------------------------------------------
+ * | YUV / PRIV | s720p | JPEG | s1440p |
+ * -----------------------------------------------------
+ *
+ * where:
+ * s720p - min (max output resolution for the given format, 1280 X 720)
+ * s1440p - min (max output resolution for the given format, 1920 X 1440)
+ *
* @return status Status code for the operation
* @return cameraIds a list of camera id combinations that support
* concurrent stream configurations with the minimum guarantees
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 3c1a810..f1db89d 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -375,7 +375,10 @@
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.radio.config</name>
- <version>1.3</version>
+ <!--
+ See compatibility_matrix.4.xml on versioning of radio config HAL.
+ -->
+ <version>1.1</version>
<interface>
<name>IRadioConfig</name>
<instance>default</instance>
diff --git a/contexthub/1.1/IContexthub.hal b/contexthub/1.1/IContexthub.hal
index 842d4b7..a3b4bd4 100644
--- a/contexthub/1.1/IContexthub.hal
+++ b/contexthub/1.1/IContexthub.hal
@@ -27,5 +27,5 @@
* @param setting User setting that has been modified.
* @param newValue The update value of the user setting.
*/
- oneway onSettingChanged(Setting setting, SettingValue newValue);
+ onSettingChanged(Setting setting, SettingValue newValue);
};
\ No newline at end of file
diff --git a/current.txt b/current.txt
index 8d14fa3..42f073b 100644
--- a/current.txt
+++ b/current.txt
@@ -638,20 +638,20 @@
07d0a252b2d8fa35887908a996ba395cf392968395fc30afab791f46e0c22a52 android.hardware.boot@1.1::IBootControl
74049a402be913963edfdd80828a53736570e9d8124a1bf18166b6ed46a6b0ab android.hardware.boot@1.1::types
b8c63679e1a3874b356f3e691aecce1191d38f59063cf2ed2dce8a9d4cabf00e android.hardware.camera.device@3.6::ICameraDevice
-e88840e0558439cb54837514ddccd43877094951758f367e9c638084eb7455a6 android.hardware.camera.provider@2.6::ICameraProvider
+5d544a22e5a54568e64f32010eb001fcfbe5547b66cff7f3330da59a23bd4f48 android.hardware.camera.provider@2.6::ICameraProvider
8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener
f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService
4d85e814f94949dae4dc6cb82bbd7d6bb24ffafda6ddb2eac928d2a4fc2e21ce android.hardware.cas@1.2::types
-2a6b1a1202493f0a7c88d716da6e2cdd66bfd77a170621791fdf14997fdfcaf9 android.hardware.contexthub@1.1::IContexthub
+8351cc01eed4c0b4482d9572b5c7ddfd17874d8edb51d6761d348116fc91dd18 android.hardware.contexthub@1.1::IContexthub
3581d0ba61663cdd45807494dcd697d01c074f27587df9140655f94346969cfe android.hardware.contexthub@1.1::types
66931c2506fbb5af61f20138cb05e0a09e7bf67d6964c231d27c648933bb33ec android.hardware.drm@1.3::ICryptoFactory
994d08ab27d613022c258a9ec48cece7adf2a305e92df5d76ef923e2c6665f64 android.hardware.drm@1.3::IDrmFactory
d9df99be0f59d8f33a9699fe316c67bfd11818aa69440bb1123ba43e717cea85 android.hardware.dumpstate@1.1::types
186bc152ae189ab48f3a761a44ddf5edd0d483073c5b6ca1f802f8b50488b754 android.hardware.dumpstate@1.1::IDumpstateDevice
769d346927a94fd40ee80a5a976d8d15cf022ef99c5900738f4a82f26c0ed229 android.hardware.gnss@2.1::types
-626db710bf917ecf551a0b0b1f25be10bf52758f43e0fc808b148b6aae2ef73e android.hardware.gnss@2.1::IGnss
+c319e68b03829958404402c2d9c682019678087d60495807c0a7444e0a6af981 android.hardware.gnss@2.1::IGnss
ba5ac712b2a656dc07c83ab4a7a2c2f3bee1bbcb752e8b8ffa9b672f3b5b0728 android.hardware.gnss@2.1::IGnssAntennaInfo
0bc3ed97cbc3f6abc89c68f4e9f4d124f9f723431997dc88c2186cf4d2ad47ee android.hardware.gnss@2.1::IGnssAntennaInfoCallback
3541d83adfeac16ee3e45d183a58dffe06012ccb5aa5bcd2e4f6eeae269f69cd android.hardware.gnss@2.1::IGnssCallback
@@ -659,10 +659,10 @@
7913a11206a577b12ade86a7cf3f95c2639cb514d086673f279bf99238c9917e android.hardware.gnss@2.1::IGnssMeasurement
0a16e5913e94d995cfcf959a1c6f10b0b8e9dfdb5f45ac6e7244711ddd740272 android.hardware.gnss@2.1::IGnssMeasurementCallback
6670e7780803a8c696c6391fda5589a334b1b37dc7be9393792ed35035413633 android.hardware.gnss.measurement_corrections@1.1::IMeasurementCorrections
-a28d6c29a7e36976acffb018208e65b3496d9152d57d864038556cdd83b35744 android.hardware.gnss.measurement_corrections@1.1::types
+956c1576ca0d6f11b42980ef59052062836b6763fe973af6cb709da50787f710 android.hardware.gnss.measurement_corrections@1.1::types
ce8dbe76eb9ee94b46ef98f725be992e760a5751073d4f4912484026541371f3 android.hardware.health@2.1::IHealth
26f04510a0b57aba5167c5c0a7c2f077c2acbb98b81902a072517829fd9fd67f android.hardware.health@2.1::IHealthInfoCallback
-3a4e7462a12589bd219599de59663d0ba9915313f45150774780d09f4e114f74 android.hardware.health@2.1::types
+e2f8bc1868fd4a3fd587c172773ea5a8c2f5a3deaf7958394102ca455252b255 android.hardware.health@2.1::types
0589e410f519e36514e7ece18f283f022df0f70efd2c12821d822f67f74aba98 android.hardware.identity@1.0::types
bbeee9604128ede83ee755b67e73b5ad29e6e1dbac9ec41fea6ffe2745b0c50a android.hardware.identity@1.0::IIdentityCredential
96ce8aad80f4c476f25261f790d357c117e79e18474c7dadd850dac704bbe65e android.hardware.identity@1.0::IIdentityCredentialStore
@@ -673,11 +673,11 @@
df9c79c4fdde2821550c6d5c3d07f5ec0adfb1b702561ce543c906ddef698703 android.hardware.media.c2@1.1::IComponent
a3eddd9bbdc87e8c22764070037dd1154f1cf006e6fba93364c4f85d4c134a19 android.hardware.media.c2@1.1::IComponentStore
65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
-9db064ee44268a876be0367ff771e618362d39ec603b6ecab17e1575725fcd87 android.hardware.neuralnetworks@1.3::IDevice
-4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
-2fa3679ad7c94b5e88724adcd560c561041068a4ca565c63830e68101988746a android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
-43088ffc71945b463a7279262cfe2e290f6ed2f15d3fd6032798a3be299fb08f android.hardware.neuralnetworks@1.3::IPreparedModel
-0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
+278817920bfd5292a7713f97f1832cca53de3de640f7670e413d97c6e7fd581c android.hardware.neuralnetworks@1.3::IDevice
+127ba11efb8220dc3aec9a8f441b59eaf1c68d7f03f577833e1824de75a36b17 android.hardware.neuralnetworks@1.3::IExecutionCallback
+6e904be0ddca5ae1de8eba020e6c38ed935ea7d80cd08f47787f137a0ca58555 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+2b0b10d2ea7a18a4048cd0eb83d35c19a817aeee95f65807fc31f4ef21381397 android.hardware.neuralnetworks@1.3::IPreparedModel
+eee3430cc86c97c7b407495863d8fb61da6f1a64b7721e77b9b4909b11b174e9 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
dd39887aa4fb60ce60ea9cc043edeadbbae6e922d09d3946311b0b410024ae14 android.hardware.neuralnetworks@1.3::types
3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
@@ -691,7 +691,7 @@
7fefa2cc5b3b3be10b5cff5c5dc195385f491d4bf23ca65f9c6b3c30c8753a33 android.hardware.radio@1.5::IRadio
e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
829d3827eeb5a8f563e80fe627419b3231012fc02bc2e79782ec5e9ad9f799a4 android.hardware.radio@1.5::IRadioResponse
-dcc8872337f0135e81970e1d8d5fd7139160dc80e9be76f0ae05290fa7e472b8 android.hardware.radio.config@1.3::types
+4c4ce691df02faa28c0729e2a033ec464e1d72699be8bcde4dfb141313dbeba8 android.hardware.radio.config@1.3::types
a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
diff --git a/gnss/2.1/IGnss.hal b/gnss/2.1/IGnss.hal
index e4da507..a880b3f 100644
--- a/gnss/2.1/IGnss.hal
+++ b/gnss/2.1/IGnss.hal
@@ -45,9 +45,9 @@
/**
* This method returns the IGnssMeasurement interface.
*
- * At least one of getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
+ * getExtensionGnssMeasurement(), getExtensionGnssMeasurement_1_1(),
* getExtensionGnssMeasurement_2_0(), and getExtensionGnssMeasurement_2_1() methods must return
- * a non-null handle, and the other methods must return nullptr.
+ * non-null. They can all return the same, latest version of IGnssMeasurement.
*
* @return gnssMeasurementIface Handle to the IGnssMeasurement interface.
*/
@@ -56,9 +56,9 @@
/**
* This method returns the IGnssConfiguration interface.
*
- * At least one of getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
+ * getExtensionGnssConfiguration(), getExtensionGnssConfiguration_1_1(),
* getExtensionGnssConfiguration_2_0(), and getExtensionGnssConfiguration_2_1() methods must
- * return a non-null handle, and the other methods must return nullptr.
+ * return non-null. They can all return the same, latest version of IGnssConfiguration.
*
* @return gnssConfigurationIface Handle to the IGnssConfiguration interface.
*/
diff --git a/gnss/2.1/default/GnssMeasurementCorrections.cpp b/gnss/2.1/default/GnssMeasurementCorrections.cpp
index 9dedbf6..accf62b 100644
--- a/gnss/2.1/default/GnssMeasurementCorrections.cpp
+++ b/gnss/2.1/default/GnssMeasurementCorrections.cpp
@@ -82,19 +82,20 @@
static_cast<int>(corrections.v1_0.satCorrections.size()),
corrections.hasEnvironmentBearing, corrections.environmentBearingDegrees,
corrections.environmentBearingUncertaintyDegrees);
- for (auto singleSatCorrection : corrections.v1_0.satCorrections) {
+ for (auto singleSatCorrection : corrections.satCorrections) {
ALOGD("singleSatCorrection = flags: %d, constellation: %d, svid: %d, cfHz: %f, probLos: %f,"
" epl: %f, eplUnc: %f",
- static_cast<int>(singleSatCorrection.singleSatCorrectionFlags),
+ static_cast<int>(singleSatCorrection.v1_0.singleSatCorrectionFlags),
static_cast<int>(singleSatCorrection.constellation),
- static_cast<int>(singleSatCorrection.svid), singleSatCorrection.carrierFrequencyHz,
- singleSatCorrection.probSatIsLos, singleSatCorrection.excessPathLengthMeters,
- singleSatCorrection.excessPathLengthUncertaintyMeters);
+ static_cast<int>(singleSatCorrection.v1_0.svid),
+ singleSatCorrection.v1_0.carrierFrequencyHz, singleSatCorrection.v1_0.probSatIsLos,
+ singleSatCorrection.v1_0.excessPathLengthMeters,
+ singleSatCorrection.v1_0.excessPathLengthUncertaintyMeters);
ALOGD("reflecting plane = lat: %f, lng: %f, alt: %f, azm: %f",
- singleSatCorrection.reflectingPlane.latitudeDegrees,
- singleSatCorrection.reflectingPlane.longitudeDegrees,
- singleSatCorrection.reflectingPlane.altitudeMeters,
- singleSatCorrection.reflectingPlane.azimuthDegrees);
+ singleSatCorrection.v1_0.reflectingPlane.latitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.longitudeDegrees,
+ singleSatCorrection.v1_0.reflectingPlane.altitudeMeters,
+ singleSatCorrection.v1_0.reflectingPlane.azimuthDegrees);
}
return true;
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index b6c3f5e..4b5a50f 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -22,7 +22,9 @@
namespace gnss {
namespace common {
-using V1_0::GnssConstellationType;
+using GnssConstellationType_V1_0 = V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = V2_0::GnssConstellationType;
+
using V1_0::GnssLocationFlags;
void Utils::checkLocation(const GnssLocation& location, bool check_speed,
@@ -100,12 +102,12 @@
.azimuthDegrees = 203.0,
};
- SingleSatCorrection singleSatCorrection1 = {
+ SingleSatCorrection_V1_0 singleSatCorrection1 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC |
GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 12,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.50001,
@@ -113,11 +115,11 @@
.excessPathLengthUncertaintyMeters = 25.5,
.reflectingPlane = reflectingPlane,
};
- SingleSatCorrection singleSatCorrection2 = {
+ SingleSatCorrection_V1_0 singleSatCorrection2 = {
.singleSatCorrectionFlags = GnssSingleSatCorrectionFlags::HAS_SAT_IS_LOS_PROBABILITY |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH |
GnssSingleSatCorrectionFlags::HAS_EXCESS_PATH_LENGTH_UNC,
- .constellation = GnssConstellationType::GPS,
+ .constellation = GnssConstellationType_V1_0::GPS,
.svid = 9,
.carrierFrequencyHz = 1.59975e+09,
.probSatIsLos = 0.873,
@@ -125,8 +127,8 @@
.excessPathLengthUncertaintyMeters = 10.0,
};
- hidl_vec<SingleSatCorrection> singleSatCorrections = {singleSatCorrection1,
- singleSatCorrection2};
+ hidl_vec<SingleSatCorrection_V1_0> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
MeasurementCorrections_1_0 mockCorrections = {
.latitudeDegrees = 37.4219999,
.longitudeDegrees = -122.0840575,
@@ -142,11 +144,27 @@
const MeasurementCorrections_1_1 Utils::getMockMeasurementCorrections_1_1() {
MeasurementCorrections_1_0 mockCorrections_1_0 = getMockMeasurementCorrections();
+ SingleSatCorrection_V1_1 singleSatCorrection1 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[0],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+ SingleSatCorrection_V1_1 singleSatCorrection2 = {
+ .v1_0 = mockCorrections_1_0.satCorrections[1],
+ .constellation = GnssConstellationType_V2_0::IRNSS,
+ };
+
+ mockCorrections_1_0.satCorrections[0].constellation = GnssConstellationType_V1_0::UNKNOWN;
+ mockCorrections_1_0.satCorrections[1].constellation = GnssConstellationType_V1_0::UNKNOWN;
+
+ hidl_vec<SingleSatCorrection_V1_1> singleSatCorrections = {singleSatCorrection1,
+ singleSatCorrection2};
+
MeasurementCorrections_1_1 mockCorrections_1_1 = {
.v1_0 = mockCorrections_1_0,
.hasEnvironmentBearing = true,
.environmentBearingDegrees = 45.0,
.environmentBearingUncertaintyDegrees = 4.0,
+ .satCorrections = singleSatCorrections,
};
return mockCorrections_1_1;
}
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index 781ad42..c3cdd18 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -29,6 +29,11 @@
using MeasurementCorrections_1_1 =
android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
+using SingleSatCorrection_V1_0 =
+ android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using SingleSatCorrection_V1_1 =
+ android::hardware::gnss::measurement_corrections::V1_1::SingleSatCorrection;
+
namespace android {
namespace hardware {
namespace gnss {
diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp
index 1d69f20..d279af6 100644
--- a/gnss/measurement_corrections/1.1/Android.bp
+++ b/gnss/measurement_corrections/1.1/Android.bp
@@ -12,6 +12,7 @@
],
interfaces: [
"android.hardware.gnss.measurement_corrections@1.0",
+ "android.hardware.gnss@2.0",
"android.hardware.gnss@1.0",
"android.hidl.base@1.0",
],
diff --git a/gnss/measurement_corrections/1.1/types.hal b/gnss/measurement_corrections/1.1/types.hal
index f945c57..e98be13 100644
--- a/gnss/measurement_corrections/1.1/types.hal
+++ b/gnss/measurement_corrections/1.1/types.hal
@@ -17,11 +17,14 @@
package android.hardware.gnss.measurement_corrections@1.1;
import @1.0::MeasurementCorrections;
+import @1.0::SingleSatCorrection;
+import android.hardware.gnss@2.0::GnssConstellationType;
/**
* A struct containing a set of measurement corrections for all used GNSS satellites at the location
* specified by latitudeDegrees, longitudeDegrees, altitudeMeters and at the time of week specified
- * toaGpsNanosecondsOfWeek
+ * toaGpsNanosecondsOfWeek. The v1_0.satCorrections field is deprecated and is no longer used by
+ * framework.
*/
struct MeasurementCorrections {
@1.0::MeasurementCorrections v1_0;
@@ -64,4 +67,25 @@
* before calling this method. The value is undefined if hasEnvironmentBearing is false.
*/
float environmentBearingUncertaintyDegrees;
+
+ /**
+ * A set of SingleSatCorrection each containing measurement corrections for a satellite in view
+ */
+ vec<SingleSatCorrection> satCorrections;
+};
+
+/**
+ * A struct with measurement corrections for a single visible satellites, updating the
+ * GnssConstellationType to 2.0, which supports IRNSS. The v1_0.constellation is deprecated and is
+ * no longer used by framework.
+ *
+ * The bit mask singleSatCorrectionFlags indicates which correction values are valid in the struct
+ */
+struct SingleSatCorrection {
+ @1.0::SingleSatCorrection v1_0;
+
+ /**
+ * Defines the constellation of the given satellite.
+ */
+ GnssConstellationType constellation;
};
diff --git a/health/2.1/types.hal b/health/2.1/types.hal
index be1eaf8..d775491 100644
--- a/health/2.1/types.hal
+++ b/health/2.1/types.hal
@@ -19,6 +19,10 @@
import @1.0::HealthConfig;
import @2.0::HealthInfo;
+enum Constants : int64_t {
+ BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED = -1,
+};
+
/**
* Battery capacity level. This enum provides additional information along side
* with the battery capacity.
@@ -27,11 +31,17 @@
*/
enum BatteryCapacityLevel : int32_t {
/**
+ * Battery capacity level is unsupported.
+ * Battery capacity level must be set to this value if and only if the
+ * implementation is unsupported.
+ */
+ UNSUPPORTED = -1,
+ /**
* Battery capacity level is unknown.
* Battery capacity level must be set to this value if and only if battery
- * is not present.
+ * is not present or the battery capacity level is unknown/uninitialized.
*/
- UNKNOWN = 0,
+ UNKNOWN,
/**
* Battery is at critical level. The Android framework must schedule a
* shutdown when it sees this value from the HAL.
@@ -78,6 +88,8 @@
/**
* Estimated time to fully charge the device (in seconds).
+ * Value must be BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED if and
+ * only if the implementation is unsupported.
* Value must be 0 if and only if batteryCapacityLevel is FULL or UNKNOWN.
* Otherwise, value must be positive.
*/
diff --git a/identity/1.0/Android.bp b/identity/1.0/Android.bp
deleted file mode 100644
index e0a6332..0000000
--- a/identity/1.0/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.identity@1.0",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IIdentityCredential.hal",
- "IIdentityCredentialStore.hal",
- "IWritableIdentityCredential.hal",
- ],
- interfaces: [
- "android.hardware.keymaster@4.0",
- "android.hidl.base@1.0",
- ],
- gen_java: false,
-}
diff --git a/identity/1.0/default/Android.bp b/identity/1.0/default/Android.bp
deleted file mode 100644
index d2b2966..0000000
--- a/identity/1.0/default/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_binary {
- name: "android.hardware.identity@1.0-service.example",
- init_rc: ["android.hardware.identity@1.0-service.example.rc"],
- vendor: true,
- relative_install_path: "hw",
- cflags: [
- "-Wall",
- "-Wextra",
- ],
- srcs: [
- "service.cpp",
- "IdentityCredential.cpp",
- "IdentityCredentialStore.cpp",
- "WritableIdentityCredential.cpp",
- ],
- shared_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- "libcrypto",
- "libbase",
- "libhidlbase",
- "liblog",
- "libutils",
- ],
-}
diff --git a/identity/1.0/default/IdentityCredential.cpp b/identity/1.0/default/IdentityCredential.cpp
deleted file mode 100644
index b0a5e56..0000000
--- a/identity/1.0/default/IdentityCredential.cpp
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IdentityCredential"
-
-#include "IdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::keymaster::V4_0::Timestamp;
-using ::std::optional;
-
-Return<void> IdentityCredential::deleteCredential(deleteCredential_cb _hidl_cb) {
- cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
- vector<uint8_t> proofOfDeletion = array.encode();
-
- optional<vector<uint8_t>> proofOfDeletionSignature =
- support::coseSignEcDsa(credentialPrivKey_,
- proofOfDeletion, // payload
- {}, // additionalData
- {}); // certificateChain
- if (!proofOfDeletionSignature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {});
- return Void();
- }
-
- _hidl_cb(support::resultOK(), proofOfDeletionSignature.value());
- return Void();
-}
-
-Return<void> IdentityCredential::createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating ephemeral key pair"), {});
- return Void();
- }
-
- // Stash public key of this key-pair for later check in startRetrieval().
- optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!publicKey) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error getting public part of ephemeral key pair"),
- {});
- return Void();
- }
- ephemeralPublicKey_ = publicKey.value();
-
- _hidl_cb(support::resultOK(), keyPair.value());
- return Void();
-}
-
-Return<void> IdentityCredential::setReaderEphemeralPublicKey(
- const hidl_vec<uint8_t>& publicKey, setReaderEphemeralPublicKey_cb _hidl_cb) {
- readerPublicKey_ = publicKey;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-ResultCode IdentityCredential::initialize() {
- auto [item, _, message] = cppbor::parse(credentialData_);
- if (item == nullptr) {
- LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Array* arrayItem = item->asArray();
- if (arrayItem == nullptr || arrayItem->size() != 3) {
- LOG(ERROR) << "CredentialData is not an array with three elements";
- return ResultCode::INVALID_DATA;
- }
-
- const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
- const cppbor::Bool* testCredentialItem =
- ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
- : nullptr);
- const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
- if (docTypeItem == nullptr || testCredentialItem == nullptr ||
- encryptedCredentialKeysItem == nullptr) {
- LOG(ERROR) << "CredentialData unexpected item types";
- return ResultCode::INVALID_DATA;
- }
-
- docType_ = docTypeItem->value();
- testCredential_ = testCredentialItem->value();
-
- vector<uint8_t> hardwareBoundKey;
- if (testCredential_) {
- hardwareBoundKey = support::getTestHardwareBoundKey();
- } else {
- hardwareBoundKey = support::getHardwareBoundKey();
- }
-
- const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
- const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> decryptedCredentialKeys =
- support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
- if (!decryptedCredentialKeys) {
- LOG(ERROR) << "Error decrypting CredentialKeys";
- return ResultCode::INVALID_DATA;
- }
-
- auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
- if (dckItem == nullptr) {
- LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Array* dckArrayItem = dckItem->asArray();
- if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
- LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
- return ResultCode::INVALID_DATA;
- }
- const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
- const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
- if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
- LOG(ERROR) << "CredentialKeys unexpected item types";
- return ResultCode::INVALID_DATA;
- }
- storageKey_ = storageKeyItem->value();
- credentialPrivKey_ = credentialPrivKeyItem->value();
-
- return ResultCode::OK;
-}
-
-Return<void> IdentityCredential::createAuthChallenge(createAuthChallenge_cb _hidl_cb) {
- uint64_t challenge = 0;
- while (challenge == 0) {
- optional<vector<uint8_t>> bytes = support::getRandom(8);
- if (!bytes) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random data for challenge"),
- 0);
- return Void();
- }
-
- challenge = 0;
- for (size_t n = 0; n < bytes.value().size(); n++) {
- challenge |= ((bytes.value())[n] << (n * 8));
- }
- }
-
- authChallenge_ = challenge;
- _hidl_cb(support::resultOK(), challenge);
- return Void();
-}
-
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& readerCertificateChain) {
- optional<vector<uint8_t>> acpPubKey =
- support::certificateChainGetTopMostKey(profile.readerCertificate);
- if (!acpPubKey) {
- LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
- return false;
- }
-
- optional<vector<vector<uint8_t>>> certificatesInChain =
- support::certificateChainSplit(readerCertificateChain);
- if (!certificatesInChain) {
- LOG(ERROR) << "Error splitting readerCertificateChain";
- return false;
- }
- for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
- optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
- if (!certPubKey) {
- LOG(ERROR)
- << "Error extracting public key from certificate in chain presented by reader";
- return false;
- }
- if (acpPubKey.value() == certPubKey.value()) {
- return true;
- }
- }
- return false;
-}
-
-Timestamp clockGetTime() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time);
- return time.tv_sec * 1000 + time.tv_nsec / 1000000;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
- const HardwareAuthToken& authToken, uint64_t authChallenge) {
- if (profile.secureUserId != authToken.userId) {
- LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
- << ") differs from userId in authToken (" << authToken.userId << ")";
- return false;
- }
-
- if (profile.timeoutMillis == 0) {
- if (authToken.challenge == 0) {
- LOG(ERROR) << "No challenge in authToken";
- return false;
- }
-
- if (authToken.challenge != authChallenge) {
- LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
- return false;
- }
- return true;
- }
-
- // Note that the Epoch for timestamps in HardwareAuthToken is at the
- // discretion of the vendor:
- //
- // "[...] since some starting point (generally the most recent device
- // boot) which all of the applications within one secure environment
- // must agree upon."
- //
- // Therefore, if this software implementation is used on a device which isn't
- // the emulator then the assumption that the epoch is the same as used in
- // clockGetTime above will not hold. This is OK as this software
- // implementation should never be used on a real device.
- //
- Timestamp now = clockGetTime();
- if (authToken.timestamp > now) {
- LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp
- << ") is in the future (now: " << now << ")";
- return false;
- }
- if (now > authToken.timestamp + profile.timeoutMillis) {
- LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp << " + "
- << profile.timeoutMillis << " = "
- << (authToken.timestamp + profile.timeoutMillis)
- << ") is in the past (now: " << now << ")";
- return false;
- }
-
- return true;
-}
-
-Return<void> IdentityCredential::startRetrieval(
- const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken, const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript, const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts, startRetrieval_cb _hidl_cb) {
- if (sessionTranscript.size() > 0) {
- auto [item, _, message] = cppbor::parse(sessionTranscript);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "SessionTranscript contains invalid CBOR"));
- return Void();
- }
- sessionTranscriptItem_ = std::move(item);
- }
- if (numStartRetrievalCalls_ > 0) {
- if (sessionTranscript_ != vector<uint8_t>(sessionTranscript)) {
- _hidl_cb(support::result(
- ResultCode::SESSION_TRANSCRIPT_MISMATCH,
- "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
- return Void();
- }
- }
- sessionTranscript_ = sessionTranscript;
-
- // If there is a signature, validate that it was made with the top-most key in the
- // certificate chain embedded in the COSE_Sign1 structure.
- optional<vector<uint8_t>> readerCertificateChain;
- if (readerSignature.size() > 0) {
- readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
- if (!readerCertificateChain) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get reader certificate chain from COSE_Sign1"));
- return Void();
- }
-
- if (!support::certificateChainValidate(readerCertificateChain.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Error validating reader certificate chain"));
- return Void();
- }
-
- optional<vector<uint8_t>> readerPublicKey =
- support::certificateChainGetTopMostKey(readerCertificateChain.value());
- if (!readerPublicKey) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "Unable to get public key from reader certificate chain"));
- return Void();
- }
-
- const vector<uint8_t>& itemsRequestBytes = itemsRequest;
- vector<uint8_t> dataThatWasSigned = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscriptItem_->clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- if (!support::coseCheckEcDsaSignature(readerSignature,
- dataThatWasSigned, // detached content
- readerPublicKey.value())) {
- _hidl_cb(support::result(ResultCode::READER_SIGNATURE_CHECK_FAILED,
- "readerSignature check failed"));
- return Void();
- }
- }
-
- // Here's where we would validate the passed-in |authToken| to assure ourselves
- // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
- //
- // However this involves calculating the MAC. However this requires access
- // to the key needed to a pre-shared key which we don't have...
- //
-
- // To prevent replay-attacks, we check that the public part of the ephemeral
- // key we previously created, is present in the DeviceEngagement part of
- // SessionTranscript as a COSE_Key, in uncompressed form.
- //
- // We do this by just searching for the X and Y coordinates.
- if (sessionTranscript.size() > 0) {
- const cppbor::Array* array = sessionTranscriptItem_->asArray();
- if (array == nullptr || array->size() != 2) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "SessionTranscript is not an array with two items"));
- return Void();
- }
- const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
- if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "First item in SessionTranscript array is not a "
- "semantic with value 24"));
- return Void();
- }
- const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
- if (encodedDE == nullptr) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Child of semantic in first item in SessionTranscript "
- "array is not a bstr"));
- return Void();
- }
- const vector<uint8_t>& bytesDE = encodedDE->value();
-
- auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
- if (!getXYSuccess) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Error extracting X and Y from ePub"));
- return Void();
- }
- if (sessionTranscript.size() > 0 &&
- !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
- memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
- _hidl_cb(support::result(ResultCode::EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
- "Did not find ephemeral public key's X and Y coordinates in "
- "SessionTranscript (make sure leading zeroes are not used)"));
- return Void();
- }
- }
-
- // itemsRequest: If non-empty, contains request data that may be signed by the
- // reader. The content can be defined in the way appropriate for the
- // credential, but there are three requirements that must be met to work with
- // this HAL:
- if (itemsRequest.size() > 0) {
- // 1. The content must be a CBOR-encoded structure.
- auto [item, _, message] = cppbor::parse(itemsRequest);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Error decoding CBOR in itemsRequest: %s", message.c_str()));
- return Void();
- }
-
- // 2. The CBOR structure must be a map.
- const cppbor::Map* map = item->asMap();
- if (map == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "itemsRequest is not a CBOR map"));
- return Void();
- }
-
- // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
- // the example below.
- //
- // NameSpaces = {
- // + NameSpace => DataElements ; Requested data elements for each NameSpace
- // }
- //
- // NameSpace = tstr
- //
- // DataElements = {
- // + DataElement => IntentToRetain
- // }
- //
- // DataElement = tstr
- // IntentToRetain = bool
- //
- // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
- // through 3.:
- //
- // {
- // 'docType' : 'org.iso.18013-5.2019',
- // 'nameSpaces' : {
- // 'org.iso.18013-5.2019' : {
- // 'Last name' : false,
- // 'Birth date' : false,
- // 'First name' : false,
- // 'Home address' : true
- // },
- // 'org.aamva.iso.18013-5.2019' : {
- // 'Real Id' : false
- // }
- // }
- // }
- //
- const cppbor::Map* nsMap = nullptr;
- for (size_t n = 0; n < map->size(); n++) {
- const auto& [keyItem, valueItem] = (*map)[n];
- if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
- valueItem->type() == cppbor::MAP) {
- nsMap = valueItem->asMap();
- break;
- }
- }
- if (nsMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "No nameSpaces map in top-most map"));
- return Void();
- }
-
- for (size_t n = 0; n < nsMap->size(); n++) {
- auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
- const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
- const cppbor::Map* nsInnerMap = nsValueItem->asMap();
- if (nsKey == nullptr || nsInnerMap == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in nameSpaces map"));
- return Void();
- }
- string requestedNamespace = nsKey->value();
- vector<string> requestedKeys;
- for (size_t m = 0; m < nsInnerMap->size(); m++) {
- const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
- const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
- const cppbor::Simple* simple = innerMapValueItem->asSimple();
- const cppbor::Bool* intentToRetainItem =
- (simple != nullptr) ? simple->asBool() : nullptr;
- if (nameItem == nullptr || intentToRetainItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_ITEMS_REQUEST_MESSAGE,
- "Type mismatch in value in nameSpaces map"));
- return Void();
- }
- requestedKeys.push_back(nameItem->value());
- }
- requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
- }
- }
-
- // Finally, validate all the access control profiles in the requestData.
- bool haveAuthToken = (authToken.mac.size() > 0);
- for (const auto& profile : accessControlProfiles) {
- if (!support::secureAccessControlProfileCheckMac(profile, storageKey_)) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Error checking MAC for profile with id %d", int(profile.id)));
- return Void();
- }
- ResultCode accessControlCheck = ResultCode::OK;
- if (profile.userAuthenticationRequired) {
- if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
- accessControlCheck = ResultCode::USER_AUTHENTICATION_FAILED;
- }
- } else if (profile.readerCertificate.size() > 0) {
- if (!readerCertificateChain ||
- !checkReaderAuthentication(profile, readerCertificateChain.value())) {
- accessControlCheck = ResultCode::READER_AUTHENTICATION_FAILED;
- }
- }
- profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
- }
-
- deviceNameSpacesMap_ = cppbor::Map();
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_ = requestCounts;
- currentNameSpace_ = "";
-
- itemsRequest_ = itemsRequest;
-
- numStartRetrievalCalls_ += 1;
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::startRetrieveEntryValue(
- const hidl_string& nameSpace, const hidl_string& name, uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds, startRetrieveEntryValue_cb _hidl_cb) {
- if (name.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name cannot be empty"));
- return Void();
- }
- if (nameSpace.empty()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Name space cannot be empty"));
- return Void();
- }
-
- if (requestCountsRemaining_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more name spaces left to go through"));
- return Void();
- }
-
- if (currentNameSpace_ == "") {
- // First call.
- currentNameSpace_ = nameSpace;
- }
-
- if (nameSpace == currentNameSpace_) {
- // Same namespace.
- if (requestCountsRemaining_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "No more entries to be retrieved in current name space"));
- return Void();
- }
- requestCountsRemaining_[0] -= 1;
- } else {
- // New namespace.
- if (requestCountsRemaining_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Moved to new name space but %d entries need to be retrieved "
- "in current name space",
- int(requestCountsRemaining_[0])));
- return Void();
- }
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
- requestCountsRemaining_.erase(requestCountsRemaining_.begin());
- currentNameSpace_ = nameSpace;
- }
-
- // It's permissible to have an empty itemsRequest... but if non-empty you can
- // only request what was specified in said itemsRequest. Enforce that.
- if (itemsRequest_.size() > 0) {
- const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
- if (it == requestedNameSpacesAndNames_.end()) {
- _hidl_cb(support::result(ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Name space '%s' was not requested in startRetrieval",
- nameSpace.c_str()));
- return Void();
- }
- const auto& dataItemNames = it->second;
- if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
- _hidl_cb(support::result(
- ResultCode::NOT_IN_REQUEST_MESSAGE,
- "Data item name '%s' in name space '%s' was not requested in startRetrieval",
- name.c_str(), nameSpace.c_str()));
- return Void();
- }
- }
-
- // Enforce access control.
- //
- // Access is granted if at least one of the profiles grants access.
- //
- // If an item is configured without any profiles, access is denied.
- //
- ResultCode accessControl = ResultCode::NO_ACCESS_CONTROL_PROFILES;
- for (auto id : accessControlProfileIds) {
- auto search = profileIdToAccessCheckResult_.find(id);
- if (search == profileIdToAccessCheckResult_.end()) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Requested entry with unvalidated profile id %d", (int(id))));
- return Void();
- }
- ResultCode accessControlForProfile = search->second;
- if (accessControlForProfile == ResultCode::OK) {
- accessControl = ResultCode::OK;
- break;
- }
- accessControl = accessControlForProfile;
- }
- if (accessControl != ResultCode::OK) {
- _hidl_cb(support::result(accessControl, "Access control check failed"));
- return Void();
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- currentName_ = name;
- entryRemainingBytes_ = entrySize;
- entryValue_.resize(0);
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> IdentityCredential::retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) {
- optional<vector<uint8_t>> content =
- support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
- if (!content) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting data"), {});
- return Void();
- }
-
- size_t chunkSize = content.value().size();
-
- if (chunkSize > entryRemainingBytes_) {
- LOG(ERROR) << "Retrieved chunk of size " << chunkSize
- << " is bigger than remaining space of size " << entryRemainingBytes_;
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved chunk of size %zd is bigger than remaining space "
- "of size %zd",
- chunkSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryRemainingBytes_ -= chunkSize;
- if (entryRemainingBytes_ > 0) {
- if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- chunkSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
-
- if (entryRemainingBytes_ == 0) {
- auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
- if (entryValueItem == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Retrieved data invalid CBOR"), {});
- return Void();
- }
- currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
- }
-
- _hidl_cb(support::resultOK(), content.value());
- return Void();
-}
-
-Return<void> IdentityCredential::finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) {
- if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
- deviceNameSpacesMap_.add(currentNameSpace_,
- std::move(currentNameSpaceDeviceNameSpacesMap_));
- }
- vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
-
- // If there's no signing key or no sessionTranscript or no reader ephemeral
- // public key, we return the empty MAC.
- optional<vector<uint8_t>> mac;
- if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
- cppbor::Array array;
- array.add("DeviceAuthentication");
- array.add(sessionTranscriptItem_->clone());
- array.add(docType_);
- array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
- vector<uint8_t> encodedDeviceAuthentication = array.encode();
-
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> signingKey =
- support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Error decrypting signingKeyBlob"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(readerPublicKey_, signingKey.value());
- if (!sharedSecret) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error doing ECDH"), {}, {});
- return Void();
- }
-
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- if (!derivedKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error deriving key from shared secret"),
- {}, {});
- return Void();
- }
-
- mac = support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // additionalData
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error MACing data"), {}, {});
- return Void();
- }
- }
-
- _hidl_cb(support::resultOK(), mac.value_or(vector<uint8_t>({})), encodedDeviceNameSpaces);
- return Void();
-}
-
-Return<void> IdentityCredential::generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) {
- string serialDecimal = "0"; // TODO: set serial to something unique
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential Reference Implementation";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
- if (!signingKeyPKCS8) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingPublicKey =
- support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
- if (!signingPublicKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of signingKey"), {},
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
- if (!signingKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting private part of signingKey"),
- {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
- signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!certificate) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating signingKey"), {}, {});
- return Void();
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting random"), {}, {});
- return Void();
- }
- vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
- optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
- storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
- if (!encryptedSigningKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting signingKey"), {}, {});
- return Void();
- }
- _hidl_cb(support::resultOK(), encryptedSigningKey.value(), certificate.value());
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredential.h b/identity/1.0/default/IdentityCredential.h
deleted file mode 100644
index eb8787b..0000000
--- a/identity/1.0/default/IdentityCredential.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::map;
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-using MapStringToVectorOfStrings = map<string, vector<string>>;
-
-class IdentityCredential : public IIdentityCredential {
- public:
- IdentityCredential(const hidl_vec<uint8_t>& credentialData)
- : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
-
- // Parses and decrypts credentialData_, return false on failure. Must be
- // called right after construction.
- ResultCode initialize();
-
- // Methods from ::android::hardware::identity::IIdentityCredential follow.
-
- Return<void> deleteCredential(deleteCredential_cb _hidl_cb) override;
- Return<void> createEphemeralKeyPair(createEphemeralKeyPair_cb _hidl_cb) override;
-
- Return<void> setReaderEphemeralPublicKey(const hidl_vec<uint8_t>& publicKey,
- setReaderEphemeralPublicKey_cb _hidl_cb) override;
-
- Return<void> createAuthChallenge(createAuthChallenge_cb _hidl_cb) override;
-
- Return<void> startRetrieval(const hidl_vec<SecureAccessControlProfile>& accessControlProfiles,
- const HardwareAuthToken& authToken,
- const hidl_vec<uint8_t>& itemsRequest,
- const hidl_vec<uint8_t>& sessionTranscript,
- const hidl_vec<uint8_t>& readerSignature,
- const hidl_vec<uint16_t>& requestCounts,
- startRetrieval_cb _hidl_cb) override;
- Return<void> startRetrieveEntryValue(const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize,
- const hidl_vec<uint16_t>& accessControlProfileIds,
- startRetrieveEntryValue_cb _hidl_cb) override;
- Return<void> retrieveEntryValue(const hidl_vec<uint8_t>& encryptedContent,
- retrieveEntryValue_cb _hidl_cb) override;
- Return<void> finishRetrieval(const hidl_vec<uint8_t>& signingKeyBlob,
- finishRetrieval_cb _hidl_cb) override;
-
- Return<void> generateSigningKeyPair(generateSigningKeyPair_cb _hidl_cb) override;
-
- private:
- // Set by constructor
- vector<uint8_t> credentialData_;
- int numStartRetrievalCalls_;
-
- // Set by initialize()
- string docType_;
- bool testCredential_;
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
-
- // Set by createEphemeralKeyPair()
- vector<uint8_t> ephemeralPublicKey_;
-
- // Set by setReaderEphemeralPublicKey()
- vector<uint8_t> readerPublicKey_;
-
- // Set by createAuthChallenge()
- uint64_t authChallenge_;
-
- // Set at startRetrieval() time.
- map<uint16_t, ResultCode> profileIdToAccessCheckResult_;
- vector<uint8_t> sessionTranscript_;
- std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
- vector<uint8_t> itemsRequest_;
- vector<uint16_t> requestCountsRemaining_;
- MapStringToVectorOfStrings requestedNameSpacesAndNames_;
- cppbor::Map deviceNameSpacesMap_;
- cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
-
- // Set at startRetrieveEntryValue() time.
- string currentNameSpace_;
- string currentName_;
- size_t entryRemainingBytes_;
- vector<uint8_t> entryValue_;
- vector<uint8_t> entryAdditionalData_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/IdentityCredentialStore.cpp b/identity/1.0/default/IdentityCredentialStore.cpp
deleted file mode 100644
index 9eb1e70..0000000
--- a/identity/1.0/default/IdentityCredentialStore.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IdentityCredentialStore"
-
-#include "IdentityCredentialStore.h"
-#include "IdentityCredential.h"
-#include "WritableIdentityCredential.h"
-
-#include <android-base/logging.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-// Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
-
-Return<void> IdentityCredentialStore::getHardwareInformation(getHardwareInformation_cb _hidl_cb) {
- _hidl_cb(support::resultOK(), "IdentityCredential Reference Implementation", "Google",
- kGcmChunkSize, false /* isDirectAccess */, {} /* supportedDocTypes */);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::createCredential(const hidl_string& docType,
- bool testCredential,
- createCredential_cb _hidl_cb) {
- auto writable_credential = new WritableIdentityCredential(docType, testCredential);
- if (!writable_credential->initialize()) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error initializing WritableIdentityCredential"),
- writable_credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), writable_credential);
- return Void();
-}
-
-Return<void> IdentityCredentialStore::getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) {
- auto credential = new IdentityCredential(credentialData);
- // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
- auto ret = credential->initialize();
- if (ret != ResultCode::OK) {
- _hidl_cb(support::result(ret, "Error initializing IdentityCredential"), credential);
- return Void();
- }
- _hidl_cb(support::resultOK(), credential);
- return Void();
-}
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/default/IdentityCredentialStore.h b/identity/1.0/default/IdentityCredentialStore.h
deleted file mode 100644
index ad75360..0000000
--- a/identity/1.0/default/IdentityCredentialStore.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-
-class IdentityCredentialStore : public IIdentityCredentialStore {
- public:
- IdentityCredentialStore() {}
-
- // The GCM chunk size used by this implementation is 64 KiB.
- static constexpr size_t kGcmChunkSize = 64 * 1024;
-
- // Methods from ::android::hardware::identity::IIdentityCredentialStore follow.
- Return<void> getHardwareInformation(getHardwareInformation_cb _hidl_cb) override;
- Return<void> createCredential(const hidl_string& docType, bool testCredential,
- createCredential_cb _hidl_cb) override;
- Return<void> getCredential(const hidl_vec<uint8_t>& credentialData,
- getCredential_cb _hidl_cb) override;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/1.0/default/OWNERS b/identity/1.0/default/OWNERS
deleted file mode 100644
index 6969910..0000000
--- a/identity/1.0/default/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-swillden@google.com
-zeuthen@google.com
diff --git a/identity/1.0/default/WritableIdentityCredential.h b/identity/1.0/default/WritableIdentityCredential.h
deleted file mode 100644
index b1deb16..0000000
--- a/identity/1.0/default/WritableIdentityCredential.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-
-#include <android/hardware/identity/1.0/IWritableIdentityCredential.h>
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
-
-using ::std::string;
-using ::std::vector;
-
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
-class WritableIdentityCredential : public IWritableIdentityCredential {
- public:
- WritableIdentityCredential(const hidl_string& docType, bool testCredential)
- : docType_(docType), testCredential_(testCredential) {}
-
- // Creates the Credential Key. Returns false on failure. Must be called
- // right after construction.
- bool initialize();
-
- // Methods from ::android::hardware::identity::IWritableIdentityCredential
- // follow.
- Return<void> getAttestationCertificate(const hidl_vec<uint8_t>& attestationApplicationId,
- const hidl_vec<uint8_t>& attestationChallenge,
- getAttestationCertificate_cb _hidl_cb) override;
-
- Return<void> startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) override;
-
- Return<void> addAccessControlProfile(uint16_t id, const hidl_vec<uint8_t>& readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId,
- addAccessControlProfile_cb _hidl_cb) override;
-
- Return<void> beginAddEntry(const hidl_vec<uint16_t>& accessControlProfileIds,
- const hidl_string& nameSpace, const hidl_string& name,
- uint32_t entrySize, beginAddEntry_cb _hidl_cb) override;
-
- Return<void> addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) override;
-
- Return<void> finishAddingEntries(finishAddingEntries_cb _hidl_cb) override;
-
- private:
- string docType_;
- bool testCredential_;
-
- // These are set in initialize().
- vector<uint8_t> storageKey_;
- vector<uint8_t> credentialPrivKey_;
- vector<uint8_t> credentialPubKey_;
-
- // These fields are initialized during startPersonalization()
- size_t numAccessControlProfileRemaining_;
- vector<uint16_t> remainingEntryCounts_;
- cppbor::Array signedDataAccessControlProfiles_;
- cppbor::Map signedDataNamespaces_;
- cppbor::Array signedDataCurrentNamespace_;
-
- // These fields are initialized during beginAddEntry()
- size_t entryRemainingBytes_;
- vector<uint8_t> entryAdditionalData_;
- string entryNameSpace_;
- string entryName_;
- vector<uint16_t> entryAccessControlProfileIds_;
- vector<uint8_t> entryBytes_;
-};
-
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc b/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
deleted file mode 100644
index 1eb7319..0000000
--- a/identity/1.0/default/android.hardware.identity@1.0-service.example.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor.identity-1-0 /vendor/bin/hw/android.hardware.identity@1.0-service.example
- class hal
- user nobody
diff --git a/identity/1.0/default/service.cpp b/identity/1.0/default/service.cpp
deleted file mode 100644
index 839e803..0000000
--- a/identity/1.0/default/service.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.identity@1.0-service"
-
-#include <android-base/logging.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "IdentityCredentialStore.h"
-
-using android::hardware::joinRpcThreadpool;
-using android::hardware::identity::implementation::IdentityCredentialStore;
-
-int main(int /* argc */, char* argv[]) {
- ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
-
- ::android::base::InitLogging(argv, &android::base::StderrLogger);
-
- auto identity_store = new IdentityCredentialStore();
- auto status = identity_store->registerAsService();
- if (status != android::OK) {
- LOG(FATAL) << "Could not register service for IdentityCredentialStore 1.0 (" << status
- << ")";
- }
- joinRpcThreadpool();
- return -1; // Should never get here.
-}
diff --git a/identity/1.0/types.hal b/identity/1.0/types.hal
deleted file mode 100644
index 5aedfea..0000000
--- a/identity/1.0/types.hal
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.identity@1.0;
-
-/**
- * The ResultCode enumeration is used to convey the status of an operation.
- */
-enum ResultCode : int32_t {
- /**
- * Success.
- */
- OK = 0,
-
- /**
- * The operation failed. This is used as a generic catch-all for errors that don't belong
- * in other categories, including memory/resource allocation failures and I/O errors.
- */
- FAILED = 1,
-
- /**
- * The passed data was invalid. This is a generic catch all for errors that don't belong
- * in other categories related to parameter validation.
- */
- INVALID_DATA = 2,
-
- /**
- * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
- */
- INVALID_AUTH_TOKEN = 3,
-
- /**
- * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
- * the requirements described in the documentation for that method.
- */
- INVALID_ITEMS_REQUEST_MESSAGE = 4,
-
- /**
- * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
- * doesn't contain an embedded certificate chain, or the signature failed to
- * validate.
- */
- READER_SIGNATURE_CHECK_FAILED = 5,
-
- /**
- * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
- * key returned by createEphemeralPublicKey().
- */
- EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 6,
-
- /**
- * An access condition related to user authentication was not satisfied.
- */
- USER_AUTHENTICATION_FAILED = 7,
-
- /**
- * An access condition related to reader authentication was not satisfied.
- */
- READER_AUTHENTICATION_FAILED = 8,
-
- /**
- * The request data element has no access control profiles associated so it cannot be accessed.
- */
- NO_ACCESS_CONTROL_PROFILES = 9,
-
- /**
- * The requested data element is not in the provided non-empty itemsRequest message.
- */
- NOT_IN_REQUEST_MESSAGE = 10,
-
- /**
- * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
- */
- SESSION_TRANSCRIPT_MISMATCH = 11,
-};
-
-/**
- * A result has a ResultCode and corresponding textual message.
- */
-struct Result {
- /**
- * The result code.
- *
- * Implementations must not use values not defined in the ResultCode enumeration.
- */
- ResultCode code;
-
- /**
- * A human-readable message in English conveying more detail about a failure.
- *
- * If code is ResultCode::OK this field must be set to the empty string.
- */
- string message;
-};
-
-struct SecureAccessControlProfile {
- /**
- * id is a numeric identifier that must be unique within the context of a Credential and may be
- * used to reference the profile.
- */
- uint16_t id;
-
- /**
- * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
- * of certificates) that must be used to authenticate requests. For details about how
- * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
- */
- vec<uint8_t> readerCertificate;
-
- /**
- * if true, the user is required to authenticate to allow requests. Required authentication
- * fressness is specified by timeout below.
- *
- */
- bool userAuthenticationRequired;
-
- /**
- * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
- * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
- * is true and timout is zero then authentication is required for each reader session.
- *
- * If userAuthenticationRequired is false, timeout must be zero.
- */
- uint64_t timeoutMillis;
-
- /**
- * secureUserId must be non-zero if userAuthenticationRequired is true.
- * It is not related to any Android user ID or UID, but is created in the
- * Gatekeeper application in the secure environment.
- */
- uint64_t secureUserId;
-
- /**
- * The mac is used to authenticate the access control profile. It contains:
- *
- * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
- *
- * where AccessControlProfile is the CBOR map:
- *
- * AccessControlProfile = {
- * "id": uint,
- * ? "readerCertificate" : bstr,
- * ? (
- * "userAuthenticationRequired" : bool,
- * "timeoutMillis" : uint,
- * "secureUserId" : uint
- * )
- * }
- */
- vec<uint8_t> mac;
-};
diff --git a/identity/1.0/vts/functional/Android.bp b/identity/1.0/vts/functional/Android.bp
deleted file mode 100644
index 03b49de..0000000
--- a/identity/1.0/vts/functional/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_test {
- name: "VtsHalIdentityCredentialTargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "VtsHalIdentityCredentialTargetTest.cpp",
- ],
- static_libs: [
- "android.hardware.identity@1.0",
- "android.hardware.identity-support-lib",
- "android.hardware.keymaster@4.0",
- "libcppbor",
- ],
- shared_libs: [
- "libcrypto",
- ],
- test_suites: [
- "general-tests",
- "vts-core",
- ],
-}
diff --git a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp b/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
deleted file mode 100644
index 88b06df..0000000
--- a/identity/1.0/vts/functional/VtsHalIdentityCredentialTargetTest.cpp
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IdentityCredentialHidlHalTest"
-
-#include <map>
-
-#include <android-base/logging.h>
-#include <android/hardware/identity/1.0/IIdentityCredentialStore.h>
-#include <android/hardware/identity/1.0/types.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-namespace android {
-namespace hardware {
-namespace identity {
-namespace test {
-
-using ::android::hardware::identity::V1_0::IIdentityCredential;
-using ::android::hardware::identity::V1_0::IIdentityCredentialStore;
-using ::android::hardware::identity::V1_0::IWritableIdentityCredential;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
-// ---------------------------------------------------------------------------
-// Test Data.
-// ---------------------------------------------------------------------------
-
-struct TestEntryData {
- TestEntryData(string nameSpace, string name, vector<uint16_t> profileIds)
- : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
-
- TestEntryData(string nameSpace, string name, const string& value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
- }
- TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
- vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bstr(value).encode();
- }
- TestEntryData(string nameSpace, string name, bool value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- valueCbor = cppbor::Bool(value).encode();
- }
- TestEntryData(string nameSpace, string name, int64_t value, vector<uint16_t> profileIds)
- : TestEntryData(nameSpace, name, profileIds) {
- if (value >= 0) {
- valueCbor = cppbor::Uint(value).encode();
- } else {
- valueCbor = cppbor::Nint(-value).encode();
- }
- }
-
- string nameSpace;
- string name;
- vector<uint8_t> valueCbor;
- vector<uint16_t> profileIds;
-};
-
-struct TestProfile {
- uint16_t id;
- hidl_vec<uint8_t> readerCertificate;
- bool userAuthenticationRequired;
- uint64_t timeoutMillis;
-};
-
-/************************************
- * TEST DATA FOR AUTHENTICATION
- ************************************/
-// Test authentication token for user authentication
-
-class IdentityCredentialStoreHidlTest : public ::testing::TestWithParam<std::string> {
- public:
- virtual void SetUp() override {
- string serviceName = GetParam();
- ASSERT_FALSE(serviceName.empty());
- credentialStore_ = IIdentityCredentialStore::getService(serviceName);
- ASSERT_NE(credentialStore_, nullptr);
-
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */,
- const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- dataChunkSize_ = chunkSize;
- });
- }
- virtual void TearDown() override {}
-
- uint32_t dataChunkSize_ = 0;
-
- sp<IIdentityCredentialStore> credentialStore_;
-};
-
-TEST_P(IdentityCredentialStoreHidlTest, HardwareConfiguration) {
- credentialStore_->getHardwareInformation(
- [&](const Result& result, const hidl_string& credentialStoreName,
- const hidl_string& credentialStoreAuthorName, uint32_t chunkSize,
- bool /* isDirectAccess */, const hidl_vec<hidl_string> /* supportedDocTypes */) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(credentialStoreName.size(), 0u);
- ASSERT_GT(credentialStoreAuthorName.size(), 0u);
- ASSERT_GE(chunkSize, 256u); // Chunk sizes < APDU buffer won't be supported
- });
-}
-
-TEST_P(IdentityCredentialStoreHidlTest, createAndRetrieveCredential) {
- // First, generate a key-pair for the reader since its public key will be
- // part of the request data.
- optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
- ASSERT_TRUE(readerKeyPKCS8);
- optional<vector<uint8_t>> readerPublicKey =
- support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
- optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
- string serialDecimal = "1234";
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential VTS Test";
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
- optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
- readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- ASSERT_TRUE(readerCertificate);
-
- // Make the portrait image really big (just shy of 256 KiB) to ensure that
- // the chunking code gets exercised.
- vector<uint8_t> portraitImage;
- portraitImage.resize(256 * 1024 - 10);
- for (size_t n = 0; n < portraitImage.size(); n++) {
- portraitImage[n] = (uint8_t)n;
- }
-
- // Access control profiles:
- const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
- {0, readerCertificate.value(), false, 0},
- // Profile 1 (no authentication)
- {1, {}, false, 0}};
-
- HardwareAuthToken authToken = {};
-
- // Here's the actual test data:
- const vector<TestEntryData> testEntries = {
- {"PersonalData", "Last name", string("Turing"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Birth date", string("19120623"), vector<uint16_t>{0, 1}},
- {"PersonalData", "First name", string("Alan"), vector<uint16_t>{0, 1}},
- {"PersonalData", "Home address", string("Maida Vale, London, England"),
- vector<uint16_t>{0}},
- {"Image", "Portrait image", portraitImage, vector<uint16_t>{0, 1}},
- };
- const vector<uint16_t> testEntriesEntryCounts = {static_cast<uint16_t>(testEntries.size() - 1),
- 1u};
-
- string cborPretty;
- sp<IWritableIdentityCredential> writableCredential;
-
- hidl_vec<uint8_t> empty{0};
-
- string docType = "org.iso.18013-5.2019.mdl";
- bool testCredential = true;
- Result result;
- credentialStore_->createCredential(
- docType, testCredential,
- [&](const Result& _result, const sp<IWritableIdentityCredential>& _writableCredential) {
- result = _result;
- writableCredential = _writableCredential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(writableCredential, nullptr);
-
- string challenge = "attestationChallenge";
- // TODO: set it to something random and check it's in the cert chain
- vector<uint8_t> attestationApplicationId = {};
- vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
- vector<uint8_t> attestationCertificate;
- writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge,
- [&](const Result& _result, const hidl_vec<hidl_vec<uint8_t>>& _splitCertChain) {
- result = _result;
- vector<vector<uint8_t>> splitCerts;
- std::copy(_splitCertChain.begin(), _splitCertChain.end(),
- std::back_inserter(splitCerts));
- attestationCertificate = support::certificateChainJoin(splitCerts);
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<SecureAccessControlProfile> returnedSecureProfiles;
- for (const auto& testProfile : testProfiles) {
- SecureAccessControlProfile profile;
- writableCredential->addAccessControlProfile(
- testProfile.id, testProfile.readerCertificate,
- testProfile.userAuthenticationRequired, testProfile.timeoutMillis,
- 0, // secureUserId
- [&](const Result& _result, const SecureAccessControlProfile& _profile) {
- result = _result;
- profile = _profile;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_EQ(testProfile.id, profile.id);
- ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate);
- ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
- ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
- ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
- returnedSecureProfiles.push_back(profile);
- }
-
- // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
- // is a little hacky but it works well enough.
- map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
-
- for (const auto& entry : testEntries) {
- vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize_);
-
- writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
- entry.valueCbor.size(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<vector<uint8_t>> encryptedChunks;
- for (const auto& chunk : chunks) {
- writableCredential->addEntryValue(
- chunk, [&](const Result& result, hidl_vec<uint8_t> encryptedContent) {
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_GT(encryptedContent.size(), 0u);
- encryptedChunks.push_back(encryptedContent);
- });
- }
- encryptedBlobs[&entry] = encryptedChunks;
- }
-
- vector<uint8_t> credentialData;
- vector<uint8_t> proofOfProvisioningSignature;
- writableCredential->finishAddingEntries(
- [&](const Result& _result, const hidl_vec<uint8_t>& _credentialData,
- const hidl_vec<uint8_t>& _proofOfProvisioningSignature) {
- result = _result;
- credentialData = _credentialData;
- proofOfProvisioningSignature = _proofOfProvisioningSignature;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- optional<vector<uint8_t>> proofOfProvisioning =
- support::coseSignGetPayload(proofOfProvisioningSignature);
- ASSERT_TRUE(proofOfProvisioning);
- cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
- EXPECT_EQ(
- "[\n"
- " 'ProofOfProvisioning',\n"
- " 'org.iso.18013-5.2019.mdl',\n"
- " [\n"
- " {\n"
- " 'id' : 0,\n"
- " 'readerCertificate' : <not printed>,\n"
- " },\n"
- " {\n"
- " 'id' : 1,\n"
- " },\n"
- " ],\n"
- " {\n"
- " 'PersonalData' : [\n"
- " {\n"
- " 'name' : 'Last name',\n"
- " 'value' : 'Turing',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Birth date',\n"
- " 'value' : '19120623',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'First name',\n"
- " 'value' : 'Alan',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " {\n"
- " 'name' : 'Home address',\n"
- " 'value' : 'Maida Vale, London, England',\n"
- " 'accessControlProfiles' : [0, ],\n"
- " },\n"
- " ],\n"
- " 'Image' : [\n"
- " {\n"
- " 'name' : 'Portrait image',\n"
- " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
- " },\n"
- " ],\n"
- " },\n"
- " true,\n"
- "]",
- cborPretty);
-
- optional<vector<uint8_t>> credentialPubKey =
- support::certificateChainGetTopMostKey(attestationCertificate);
- ASSERT_TRUE(credentialPubKey);
- EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
- {}, // Additional data
- credentialPubKey.value()));
- writableCredential = nullptr;
-
- // Now that the credential has been provisioned, read it back and check the
- // correct data is returned.
- sp<IIdentityCredential> credential;
- credentialStore_->getCredential(
- credentialData, [&](const Result& _result, const sp<IIdentityCredential>& _credential) {
- result = _result;
- credential = _credential;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- ASSERT_NE(credential, nullptr);
-
- optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
- ASSERT_TRUE(readerEphemeralKeyPair);
- optional<vector<uint8_t>> readerEphemeralPublicKey =
- support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
- credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value(),
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> ephemeralKeyPair;
- credential->createEphemeralKeyPair(
- [&](const Result& _result, const hidl_vec<uint8_t>& _ephemeralKeyPair) {
- result = _result;
- ephemeralKeyPair = _ephemeralKeyPair;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
-
- // Calculate requestData field and sign it with the reader key.
- auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
- ASSERT_TRUE(getXYSuccess);
- cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
- vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
- vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
- cppbor::Array sessionTranscript = cppbor::Array()
- .add(cppbor::Semantic(24, deviceEngagementBytes))
- .add(cppbor::Semantic(24, eReaderPubBytes));
- vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
-
- vector<uint8_t> itemsRequestBytes =
- cppbor::Map("nameSpaces",
- cppbor::Map()
- .add("PersonalData", cppbor::Map()
- .add("Last name", false)
- .add("Birth date", false)
- .add("First name", false)
- .add("Home address", true))
- .add("Image", cppbor::Map().add("Portrait image", false)))
- .encode();
- cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
- EXPECT_EQ(
- "{\n"
- " 'nameSpaces' : {\n"
- " 'PersonalData' : {\n"
- " 'Last name' : false,\n"
- " 'Birth date' : false,\n"
- " 'First name' : false,\n"
- " 'Home address' : true,\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : false,\n"
- " },\n"
- " },\n"
- "}",
- cborPretty);
- vector<uint8_t> dataToSign = cppbor::Array()
- .add("ReaderAuthentication")
- .add(sessionTranscript.clone())
- .add(cppbor::Semantic(24, itemsRequestBytes))
- .encode();
- optional<vector<uint8_t>> readerSignature =
- support::coseSignEcDsa(readerKey.value(), {}, // content
- dataToSign, // detached content
- readerCertificate.value());
- ASSERT_TRUE(readerSignature);
-
- credential->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
- sessionTranscriptBytes, readerSignature.value(),
- testEntriesEntryCounts,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- for (const auto& entry : testEntries) {
- credential->startRetrieveEntryValue(entry.nameSpace, entry.name, entry.valueCbor.size(),
- entry.profileIds,
- [&](const Result& _result) { result = _result; });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- auto it = encryptedBlobs.find(&entry);
- ASSERT_NE(it, encryptedBlobs.end());
- const vector<vector<uint8_t>>& encryptedChunks = it->second;
-
- vector<uint8_t> content;
- for (const auto& encryptedChunk : encryptedChunks) {
- vector<uint8_t> chunk;
- credential->retrieveEntryValue(
- encryptedChunk, [&](const Result& _result, const hidl_vec<uint8_t>& _chunk) {
- result = _result;
- chunk = _chunk;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- content.insert(content.end(), chunk.begin(), chunk.end());
- }
- EXPECT_EQ(content, entry.valueCbor);
- }
-
- // Generate the key that will be used to sign AuthenticatedData.
- vector<uint8_t> signingKeyBlob;
- vector<uint8_t> signingKeyCertificate;
- credential->generateSigningKeyPair([&](const Result& _result,
- const hidl_vec<uint8_t> _signingKeyBlob,
- const hidl_vec<uint8_t> _signingKeyCertificate) {
- result = _result;
- signingKeyBlob = _signingKeyBlob;
- signingKeyCertificate = _signingKeyCertificate;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
-
- vector<uint8_t> mac;
- vector<uint8_t> deviceNameSpacesBytes;
- credential->finishRetrieval(signingKeyBlob,
- [&](const Result& _result, const hidl_vec<uint8_t> _mac,
- const hidl_vec<uint8_t> _deviceNameSpacesBytes) {
- result = _result;
- mac = _mac;
- deviceNameSpacesBytes = _deviceNameSpacesBytes;
- });
- EXPECT_EQ("", result.message);
- ASSERT_EQ(ResultCode::OK, result.code);
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
- ASSERT_EQ(
- "{\n"
- " 'PersonalData' : {\n"
- " 'Last name' : 'Turing',\n"
- " 'Birth date' : '19120623',\n"
- " 'First name' : 'Alan',\n"
- " 'Home address' : 'Maida Vale, London, England',\n"
- " },\n"
- " 'Image' : {\n"
- " 'Portrait image' : <bstr size=262134 "
- "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
- " },\n"
- "}",
- cborPretty);
- // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
- // deviceNameSpacesBytes] so build up that structure
- cppbor::Array deviceAuthentication;
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
- optional<vector<uint8_t>> signingPublicKey =
- support::certificateChainGetTopMostKey(signingKeyCertificate);
- EXPECT_TRUE(signingPublicKey);
-
- // Derive the key used for MACing.
- optional<vector<uint8_t>> readerEphemeralPrivateKey =
- support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
- ASSERT_TRUE(sharedSecret);
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
- ASSERT_TRUE(derivedKey);
- optional<vector<uint8_t>> calculatedMac =
- support::coseMac0(derivedKey.value(), {}, // payload
- encodedDeviceAuthentication); // detached content
- ASSERT_TRUE(calculatedMac);
- EXPECT_EQ(mac, calculatedMac);
-}
-
-INSTANTIATE_TEST_SUITE_P(PerInstance, IdentityCredentialStoreHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- IIdentityCredentialStore::descriptor)),
- android::hardware::PrintInstanceNameToString);
-
-} // namespace test
-} // namespace identity
-} // namespace hardware
-} // namespace android
diff --git a/identity/1.0/vts/OWNERS b/identity/OWNERS
similarity index 100%
rename from identity/1.0/vts/OWNERS
rename to identity/OWNERS
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
new file mode 100644
index 0000000..72b19a1
--- /dev/null
+++ b/identity/aidl/Android.bp
@@ -0,0 +1,21 @@
+aidl_interface {
+ name: "android.hardware.identity",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/identity/*.aidl",
+ ],
+ imports: [
+ "android.hardware.keymaster",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/identity/aidl/android/hardware/identity/Certificate.aidl
similarity index 63%
copy from radio/config/1.3/IRadioConfigIndication.hal
copy to identity/aidl/android/hardware/identity/Certificate.aidl
index 9ef496c..5bbc17c 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/identity/aidl/android/hardware/identity/Certificate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.identity;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Certificate {
+ /**
+ * encodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+ *
+ * If there is no certificate, this array is empty.
+ */
+ byte[] encodedCertificate;
+}
diff --git a/identity/aidl/android/hardware/identity/CipherSuite.aidl b/identity/aidl/android/hardware/identity/CipherSuite.aidl
new file mode 100644
index 0000000..20b02a8
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/CipherSuite.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+/**
+ * Cipher suites that can be used for communication between holder and reader devices.
+ */
+@VintfStability
+@Backing(type="int")
+enum CipherSuite {
+ /**
+ * Specifies that the cipher suite that will be used to secure communications between the reader
+ * is:
+ *
+ * - ECDHE with HKDF-SHA-256 for key agreement.
+ * - AES-256 with GCM block mode for authenticated encryption (nonces are incremented by
+ * one for every message).
+ * - ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+ * man-in-the-middle attacks), signing keys are not ephemeral.
+ *
+ * At present this is the only supported cipher suite and it is mandatory for all
+ * implementations to support it.
+ */
+ CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1,
+}
diff --git a/identity/aidl/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
new file mode 100644
index 0000000..d67739d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/HardwareInformation.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+@VintfStability
+parcelable HardwareInformation {
+ /**
+ * credentialStoreName is the name of the credential store implementation.
+ */
+ @utf8InCpp String credentialStoreName;
+
+ /**
+ * credentialStoreAuthorName is the name of the credential store author.
+ */
+ @utf8InCpp String credentialStoreAuthorName;
+
+ /**
+ * dataChunkSize is the size of data chunks to be used when sending and recieving data
+ * entries. All data chunks for a data item must be this size except for the last.
+ */
+ int dataChunkSize;
+
+ /**
+ * isDirectAccess specifies whether the provisioned credential is available through
+ * direct access. Credentials provisioned in credential stores with this set
+ * to true, should use reader authentication on all data elements.
+ */
+ boolean isDirectAccess;
+
+ /**
+ * supportedDocTypes if empty, then any document type is supported, otherwise
+ * only the document types returned are supported.
+ *
+ * Document types are defined in the relevant standard for the document, for example for the
+ * for Mobile Driving License as defined by ISO 18013-5 the document type is defined to
+ * be "org.iso.18013.5.1.mDL".
+ *
+ */
+ @utf8InCpp String[] supportedDocTypes;
+}
diff --git a/identity/1.0/IIdentityCredential.hal b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
similarity index 75%
rename from identity/1.0/IIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 75f6e18..10ce4c2 100644
--- a/identity/1.0/IIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import android.hardware.keymaster@4.0::HardwareAuthToken;
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
+import android.hardware.keymaster.HardwareAuthToken;
+@VintfStability
interface IIdentityCredential {
/**
* Delete a credential.
@@ -35,10 +38,9 @@
* After this method has been called, the persistent storage used for credentialData should
* be deleted.
*
- * @return proofOfDeletionSignature is a COSE_Sign1 signature described above.
+ * @return a COSE_Sign1 signature described above.
*/
- deleteCredential()
- generates(Result result, vec<uint8_t> proofOfDeletionSignature);
+ byte[] deleteCredential();
/**
* Creates an ephemeral EC key pair, for use in establishing a seceure session with a reader.
@@ -46,39 +48,33 @@
* with the reader. The reason for generating the key pair in the secure environment is so that
* the secure environment knows what public key to expect to find in the session transcript.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return keyPair contains the unencrypted key-pair in PKCS#8 format.
+ * @return the unencrypted key-pair in PKCS#8 format.
*/
- createEphemeralKeyPair() generates (Result result, vec<uint8_t> keyPair);
+ byte[] createEphemeralKeyPair();
/**
* Sets the public part of the reader's ephemeral key pair.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
* @param publicKey contains the reader's ephemeral public key, in uncompressed form.
- *
- * @return result is OK on success or FAILED if an error occurred.
*/
- setReaderEphemeralPublicKey(vec<uint8_t> publicKey) generates (Result result);
+ void setReaderEphemeralPublicKey(in byte[] publicKey);
/**
* Creates a challenge value to be used for proving successful user authentication. This
* is included in the authToken passed to the startRetrieval() method.
*
- * This method may only be called once per instance. If called more than once, FAILED
+ * This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return challenge on success, is a non-zero number.
+ * @return challenge, a non-zero number.
*/
- createAuthChallenge() generates (Result result, uint64_t challenge);
+ long createAuthChallenge();
/**
* Start an entry retrieval process.
@@ -92,12 +88,12 @@
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
* then finally finishRetrieval()) but if this is done, the sessionTranscript parameter
* must be identical for each startRetrieval() invocation. If this is not the case, this call
- * fails with the SESSION_TRANSCRIPT_MISMATCH error.
+ * fails with the STATUS_SESSION_TRANSCRIPT_MISMATCH error.
*
- * If the provided authToken is not valid this method fails with INVALID_AUTH_TOKEN.
+ * If the provided authToken is not valid this method fails with STATUS_INVALID_AUTH_TOKEN.
*
* Each of the provided accessControlProfiles is checked in this call. If they are not
- * all valid, the call fails with INVALID_DATA.
+ * all valid, the call fails with STATUS_INVALID_DATA.
*
* For the itemsRequest parameter, the content can be defined in the way appropriate for
* the credential, but there are three requirements that must be met to work with this HAL:
@@ -108,7 +104,7 @@
* the example below.
*
* If these requirements are not met the startRetrieval() call fails with
- * INVALID_ITEMS_REQUEST_MESSAGE.
+ * STATUS_INVALID_ITEMS_REQUEST_MESSAGE.
*
* Here's an example of ItemsRequest CBOR which conforms to this requirement:
*
@@ -156,17 +152,18 @@
* 'x5chain' unprotected header element of the COSE_Sign1 structure (as as described
* in 'draft-ietf-cose-x509-04'). There will be at least one certificate in said element
* and there may be more (and if so, each certificate must be signed by its successor).
- * This is checked and if the check fails the call fails with READER_SIGNATURE_CHECK_FAILED.
+ * This is checked and if the check fails the call fails with
+ * STATUS_READER_SIGNATURE_CHECK_FAILED.
*
* The SessionTranscript CBOR is conveyed in the sessionTranscript parameter. It
* is permissible for this to be empty in which case the readerSignature parameter
- * must also be empty. If this is not the case, the call fails with FAILED.
+ * must also be empty. If this is not the case, the call fails with STATUS_FAILED.
*
* If the SessionTranscript CBOR is not empty, the X and Y coordinates of the public
* part of the key-pair previously generated by createEphemeralKeyPair() must appear
* somewhere in the bytes of DeviceEngagement structure. Both X and Y should be in
* uncompressed form. If this is not satisfied, the call fails with
- * EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
+ * STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND.
*
* @param accessControlProfiles
* Access control profiles that are required to retrieve the entries that are going to be
@@ -196,16 +193,11 @@
* will succeed (i.e. that the access control profile checks will succeed). This means that
* it's the responsibility of the caller to determine which access control checks will fail
* and remove the corresponding requests from the counts.
- *
- * @return result is OK on success. If an error occurs one of the values described above
- * will be returned.
*/
- startRetrieval(vec<SecureAccessControlProfile> accessControlProfiles,
- HardwareAuthToken authToken,
- vec<uint8_t> itemsRequest,
- vec<uint8_t> sessionTranscript,
- vec<uint8_t> readerSignature,
- vec<uint16_t> requestCounts) generates(Result result);
+ void startRetrieval(in SecureAccessControlProfile[] accessControlProfiles,
+ in HardwareAuthToken authToken,
+ in byte[] itemsRequest,
+ in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
/**
* Starts retrieving an entry, subject to access control requirements. Entries must be
@@ -213,15 +205,15 @@
*
* If the requestData parameter as passed to startRetrieval() was non-empty
* this method must only be called with entries specified in that field. If this
- * requirement is not met, the call fails with NOT_IN_REQUEST_MESSAGE.
+ * requirement is not met, the call fails with STATUS_NOT_IN_REQUEST_MESSAGE.
*
- * If nameSpace or name is empty this call fails with INVALID_DATA.
+ * If nameSpace or name is empty this call fails with STATUS_INVALID_DATA.
*
* Each access control profile for the entry is checked. If user authentication
* is required and the supplied auth token doesn't provide it the call fails
- * with USER_AUTHENTICATION_FAILED. If reader authentication is required and
+ * with STATUS_USER_AUTHENTICATION_FAILED. If reader authentication is required and
* a suitable reader certificate chain isn't presented, the call fails with
- * READER_AUTHENTICATION_FAILED.
+ * STATUS_READER_AUTHENTICATION_FAILED.
*
* It is permissible to keep retrieving values if an access control check fails.
*
@@ -231,38 +223,29 @@
*
* @param entrySize is the size of the entry value, if it's a text string or a byte string.
* It must be zero if the entry value is an integer or boolean. If this requirement
- * is not met the call fails with INVALID_DATA.
+ * is not met the call fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element. If an identifier of a profile
* is given and this profile wasn't passed to startRetrieval() this call fails
- * with INVALID_DATA.
- *
- * @return result is OK on success. Otherwise one of INVALID_DATA, FAILED,
- * USER_AUTHENTICATION_FAILED, READER_AUTHENTICATION_FAILED.
+ * with STATUS_INVALID_DATA.
*/
- startRetrieveEntryValue(string nameSpace, string name, uint32_t entrySize,
- vec<uint16_t> accessControlProfileIds)
- generates (Result result);
-
+ void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name,
+ in int entrySize, in int[] accessControlProfileIds);
/**
* Retrieves an entry value, or part of one, if the entry value is larger than gcmChunkSize.
* May only be called after startRetrieveEntry().
*
* If the passed in data is not authentic, can't be decrypted, is of the wrong size, or can't
- * be decoded, this call fails with INVALID_DATA.
+ * be decoded, this call fails with STATUS_INVALID_DATA.
*
* @param encryptedContent contains the encrypted and MACed content.
*
- * @return result is OK on success, INVALID_DATA, or FAILED if an error occurred.
- *
- * @return content is the entry value as CBOR, or part of the entry value in the case the
+ * @return the entry value as CBOR, or part of the entry value in the case the
* content exceeds gcmChunkSize in length.
*/
- retrieveEntryValue(vec<uint8_t> encryptedContent)
- generates (Result result, vec<uint8_t> content);
-
+ byte[] retrieveEntryValue(in byte[] encryptedContent);
/**
* End retrieval of data, optionally returning a message authentication code over the
@@ -273,11 +256,9 @@
*
* @param signingKeyBlob is either empty or a signingKeyBlob (see generateSigningKeyPair(),
* below) containing the signing key to use to sign the data retrieved. If this
- * is not in the right format the call fails with INVALID_DATA.
+ * is not in the right format the call fails with STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return mac is empty if signingKeyBlob or the sessionTranscript passed to
+ * @param out mac is empty if signingKeyBlob or the sessionTranscript passed to
* startRetrieval() is empty. Otherwise it is a COSE_Mac0 with empty payload
* and the detached content is set to DeviceAuthentication as defined below.
* The key used for the MAC operation is EMacKey and is derived as follows:
@@ -321,23 +302,17 @@
* DataItemValue = any
*
*
- * @return deviceNameSpaces the bytes of DeviceNameSpaces.
+ * @param out deviceNameSpaces the bytes of DeviceNameSpaces.
*/
- finishRetrieval(vec<uint8_t> signingKeyBlob)
- generates(Result result, vec<uint8_t> mac, vec<uint8_t> deviceNameSpaces);
-
+ void finishRetrieval(in byte[] signingKeyBlob, out byte[] mac, out byte[] deviceNameSpaces);
/**
* Generate a key pair to be used for signing session data and retrieved data items.
*
- * @return result is OK on success or FAILED if an error occurred.
+ * @param out signingKeyBlob contains an encrypted copy of the newly-generated private
+ * signing key.
*
- * @return signingKeyBlob contains an encrypted copy of the newly-generated private signing key.
- *
- * @return signingKeyCertificate contains an X.509 certificate for the new signing key, signed
- * by the credential key.
+ * @return an X.509 certificate for the new signing key, signed by the credential key.
*/
- generateSigningKeyPair()
- generates(Result result, vec<uint8_t> signingKeyBlob,
- vec<uint8_t> signingKeyCertificate);
-};
+ Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+}
diff --git a/identity/1.0/IIdentityCredentialStore.hal b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
similarity index 67%
rename from identity/1.0/IIdentityCredentialStore.hal
rename to identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
index 118ca6f..23cb1b7 100644
--- a/identity/1.0/IIdentityCredentialStore.hal
+++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
-import IWritableIdentityCredential;
-import IIdentityCredential;
+import android.hardware.identity.IIdentityCredential;
+import android.hardware.identity.IWritableIdentityCredential;
+import android.hardware.identity.HardwareInformation;
+import android.hardware.identity.CipherSuite;
/**
* IIdentityCredentialStore provides an interface to a secure store for user identity documents.
@@ -98,37 +100,90 @@
* define appropriate encodings, those are used. For example, X.509 certificates. Where new
* encodings are needed, CBOR is used. CBOR maps are described in CDDL notation
* (https://tools.ietf.org/html/draft-ietf-cbor-cddl-06).
+ *
+ * All binder calls in the HAL may return a ServiceSpecificException with statuses from the
+ * STATUS_* integers defined in this interface. Each method states which status can be returned
+ * and under which circumstances.
*/
+@VintfStability
interface IIdentityCredentialStore {
+ /**
+ * Success.
+ */
+ const int STATUS_OK = 0;
+
+ /**
+ * The operation failed. This is used as a generic catch-all for errors that don't belong
+ * in other categories, including memory/resource allocation failures and I/O errors.
+ */
+ const int STATUS_FAILED = 1;
+
+ /**
+ * Unsupported cipher suite.
+ */
+ const int STATUS_CIPHER_SUITE_NOT_SUPPORTED = 2;
+
+ /**
+ * The passed data was invalid. This is a generic catch all for errors that don't belong
+ * in other categories related to parameter validation.
+ */
+ const int STATUS_INVALID_DATA = 3;
+
+ /**
+ * The authToken parameter passed to IIdentityCredential.startRetrieval() is not valid.
+ */
+ const int STATUS_INVALID_AUTH_TOKEN = 4;
+
+ /**
+ * The itemsRequest parameter passed to IIdentityCredential.startRetrieval() does not meet
+ * the requirements described in the documentation for that method.
+ */
+ const int STATUS_INVALID_ITEMS_REQUEST_MESSAGE = 5;
+
+ /**
+ * The readerSignature parameter in IIdentityCredential.startRetrieval() is invalid,
+ * doesn't contain an embedded certificate chain, or the signature failed to
+ * validate.
+ */
+ const int STATUS_READER_SIGNATURE_CHECK_FAILED = 6;
+
+ /**
+ * The sessionTranscript passed to startRetrieval() did not contain the ephmeral public
+ * key returned by createEphemeralPublicKey().
+ */
+ const int STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND = 7;
+
+ /**
+ * An access condition related to user authentication was not satisfied.
+ */
+ const int STATUS_USER_AUTHENTICATION_FAILED = 8;
+
+ /**
+ * An access condition related to reader authentication was not satisfied.
+ */
+ const int STATUS_READER_AUTHENTICATION_FAILED = 9;
+
+ /**
+ * The request data element has no access control profiles associated so it cannot be accessed.
+ */
+ const int STATUS_NO_ACCESS_CONTROL_PROFILES = 10;
+
+ /**
+ * The requested data element is not in the provided non-empty itemsRequest message.
+ */
+ const int STATUS_NOT_IN_REQUEST_MESSAGE = 11;
+
+ /**
+ * The passed-in sessionTranscript doesn't match the previously passed-in sessionTranscript.
+ */
+ const int STATUS_SESSION_TRANSCRIPT_MISMATCH = 12;
/**
* Returns information about hardware.
*
- * The isDirectAccess output parameter indicates whether this credential store
- * implementation is for direct access. Credentials provisioned in credential
- * stores with this set to true, should use reader authentication on all data elements.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return credentialStoreName the name of the credential store implementation.
- *
- * @return credentialStoreAuthorName the name of the credential store author.
- *
- * @return dataChunkSize the maximum size of data chunks.
- *
- * @return isDirectAccess whether the provisioned credential is available through
- * direct access.
- *
- * @return supportedDocTypes if empty, then any document type is supported, otherwise
- * only the document types returned are supported.
+ * @return a HardwareInformation with information about the hardware.
*/
- getHardwareInformation()
- generates(Result result,
- string credentialStoreName,
- string credentialStoreAuthorName,
- uint32_t dataChunkSize,
- bool isDirectAccess,
- vec<string> supportedDocTypes);
+ HardwareInformation getHardwareInformation();
/**
* createCredential creates a new Credential. When a Credential is created, two cryptographic
@@ -147,16 +202,14 @@
* all-zeros hardware-bound key (HBK) and must set the test bit in the
* personalizationReceipt (see finishAddingEntries(), in IWritableIdentityCredential).
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return writableCredential is an IWritableIdentityCredential HIDL interface that provides
- * operations to provision a credential.
+ * @return an IWritableIdentityCredential interface that provides operations to
+ * provision a credential.
*/
- createCredential(string docType, bool testCredential)
- generates(Result result, IWritableIdentityCredential writableCredential);
+ IWritableIdentityCredential createCredential(in @utf8InCpp String docType,
+ in boolean testCredential);
/**
- * getCredential retrieves an IIdentityCredential HIDL interface which allows use of a stored
+ * getCredential retrieves an IIdentityCredential interface which allows use of a stored
* Credential.
*
* The cipher suite used to communicate with the remote verifier must also be specified. Currently
@@ -170,16 +223,17 @@
*
* Support for other cipher suites may be added in a future version of this HAL.
*
+ * This method fails with STATUS_INVALID_DATA if the passed in credentialData cannot be
+ * decoded or decrypted.
+ *
+ * @param cipherSuite is the cipher suite to use.
+ *
* @param credentialData is a CBOR-encoded structure containing metadata about the credential
* and an encrypted byte array that contains data used to secure the credential. See the
- * return argument of the same name in finishAddingEntries(), in IWritableIdentityCredential.
+ * return argument of the same name in finishAddingEntries(), in
+ * IWritableIdentityCredential.
*
- * @return result is OK on success or INVALID_DATA if the passed in credentialData
- * cannot be decoded or decrypted.
- *
- * @return credential is an IIdentityCredential HIDL interface that provides operations on the
- * Credential.
+ * @return an IIdentityCredential HIDL interface that provides operations on the Credential.
*/
- getCredential(vec<uint8_t> credentialData)
- generates (Result result, IIdentityCredential credential);
-};
+ IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
+}
diff --git a/identity/1.0/IWritableIdentityCredential.hal b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
similarity index 75%
rename from identity/1.0/IWritableIdentityCredential.hal
rename to identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index f26f763..483b0c7 100644
--- a/identity/1.0/IWritableIdentityCredential.hal
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package android.hardware.identity@1.0;
+package android.hardware.identity;
+
+import android.hardware.identity.Certificate;
+import android.hardware.identity.SecureAccessControlProfile;
/**
* IWritableIdentityCredential is used to personalize a new identity credential. Credentials cannot
* be updated or modified after creation; any changes require deletion and re-creation.
*/
+@VintfStability
interface IWritableIdentityCredential {
/**
* Gets the certificate chain for credentialKey which can be used to prove the hardware
@@ -69,31 +73,23 @@
*
* @param attestationChallenge a challenge set by the issuer to ensure freshness.
*
- * @return result is OK on success, FAILED if an error occurred.
- *
- * @return certificateChain is the X.509 certificate chain for the credentialKey
+ * @return the X.509 certificate chain for the credentialKey
*/
- getAttestationCertificate(vec<uint8_t> attestationApplicationId,
- vec<uint8_t> attestationChallenge)
- generates(Result result, vec<vec<uint8_t>> certificateChain);
+ Certificate[] getAttestationCertificate(in byte[] attestationApplicationId, in byte[] attestationChallenge);
/**
* Start the personalization process.
*
* startPersonalization must not be called more than once.
*
- * @param accessControlProfileCount specifies the number of access control profiles that will be
- * provisioned with addAccessControlProfile().
+ * @param accessControlProfileCount specifies the number of access control profiles that will
+ * be provisioned with addAccessControlProfile().
*
* @param entryCounts specifies the number of data entries that will be provisioned with
* beginAddEntry() and addEntry(). Each item in the array specifies how many entries
* will be added for each name space.
- *
- * @return result is OK on success, FAILED if an error occurred.
- *
*/
- startPersonalization(uint16_t accessControlProfileCount, vec<uint16_t> entryCounts)
- generates(Result result);
+ void startPersonalization(in int accessControlProfileCount, in int[] entryCounts);
/**
* Add an access control profile, which defines the requirements or retrieval of one or more
@@ -103,15 +99,15 @@
*
* This method must be called exactly as many times as specified in the startPersonalization()
* accessControlProfileCount parameter. If this is requirement is not met, the method fails
- * with INVALID_DATA.
+ * with STATUS_INVALID_DATA.
*
* @param id a numeric identifier that must be unique within the context of a Credential and may
* be used to reference the profile. If this is not satisfied the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of certificates)
- * that must be used to authenticate requests (see the readerSignature parameter in
- * IIdentityCredential.startRetrieval).
+ * @param readerCertificate if non-empty, specifies a X.509 certificate (or chain of
+ * certificates) that must be used to authenticate requests (see the readerSignature
+ * parameter in IIdentityCredential.startRetrieval).
*
* @param userAuthenticationRequired if true, specifies that the user is required to
* authenticate to allow requests. Required authentication freshness is specified by
@@ -121,22 +117,18 @@
* authentication (see userAuthenticationRequired above) is valid, if
* userAuthenticationRequired is true. If the timout is zero then authentication is
* required for each reader session. If userAuthenticationRequired is false, the timeout
- * must be zero. If this requirement is not met the call fails with INVALID_DATA.
+ * must be zero. If this requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param secureUserId must be non-zero if userAuthenticationRequired is true. It is not
* related to any Android user ID or UID, but is created in the Gatekeeper application
* in the secure environment. If this requirement is not met the call fails with
- * INVALID_DATA.
+ * STATUS_INVALID_DATA.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return secureAccessControlProfile is a structure with the passed-in data and MAC created
- * with storageKey for authenticating the data at a later point in time.
+ * @return a structure with the passed-in data and MAC created with storageKey for authenticating
+ * the data at a later point in time.
*/
- addAccessControlProfile(uint16_t id, vec<uint8_t> readerCertificate,
- bool userAuthenticationRequired, uint64_t timeoutMillis,
- uint64_t secureUserId)
- generates(Result result, SecureAccessControlProfile secureAccessControlProfile);
+ SecureAccessControlProfile addAccessControlProfile(in int id, in Certificate readerCertificate,
+ in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
/**
* Begins the process of adding an entry to the credential. All access control profiles must be
@@ -145,7 +137,7 @@
*
* This method must be called exactly as many times as the sum of the items in the entryCounts
* parameter specified in the startPersonalization(), and must be followed by one or more calls
- * to addEntryValue(). If this requirement is not met the method fails with INVALID_DATA.
+ * to addEntryValue(). If this requirement is not met the method fails with STATUS_INVALID_DATA.
*
* @param accessControlProfileIds specifies the set of access control profiles that can
* authorize access to the provisioned element.
@@ -155,13 +147,10 @@
* @param name is the name of the element.
*
* @param entrySize is the size of the entry value. If this requirement
- * is not met this method fails with INVALID_DATA.
- *
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
+ * is not met this method fails with STATUS_INVALID_DATA.
*/
- beginAddEntry(vec<uint16_t> accessControlProfileIds, string nameSpace,
- string name, uint32_t entrySize)
- generates(Result result);
+ void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace,
+ in @utf8InCpp String name, in int entrySize);
/**
* Continues the process of adding an entry, providing a value or part of a value.
@@ -171,18 +160,13 @@
* (see IIdentityCredentialStore.getHardwareInformation()), the caller must provide the
* value in chunks. All chunks must be exactly gcmChunkSize except the last and the sum of all
* chunk sizes must equal the value of the beginAddEntry() entrySize argument. If this
- * requirement is not met the call fails with INVALID_DATA.
+ * requirement is not met the call fails with STATUS_INVALID_DATA.
*
* @param content is the entry value, encoded as CBOR. In the case the content exceeds gcmChunkSize,
* this may be partial content up to gcmChunkSize bytes long.
*
- * @return result is OK on success, INVALID_DATA or FAILED if an error occurred.
- *
- * @return encryptedContent contains the encrypted and MACed content. For directly-available
- * credentials the contents are implementation-defined but must not exceed 32 bytes in
- * length.
- *
- * For other credentials, encryptedContent contains:
+ * @return the encrypted and MACed content. For directly-available credentials the contents are
+ * implementation-defined. For other credentials, the result contains
*
* AES-GCM-ENC(storageKey, R, Data, AdditionalData)
*
@@ -196,18 +180,15 @@
* "AccessControlProfileIds" : [ + uint ],
* }
*/
- addEntryValue(vec<uint8_t> content)
- generates(Result result, vec<uint8_t> encryptedContent);
+ byte[] addEntryValue(in byte[] content);
/**
- * Finishes adding entries and returns a signature that an issuing authority may use to validate
- * that all data was provisioned correctly.
+ * Finishes adding entries and returns a signature that an issuing authority may use to
+ * validate that all data was provisioned correctly.
*
* After this method is called, the IWritableIdentityCredential is no longer usable.
*
- * @return result is OK on success or FAILED if an error occurred.
- *
- * @return credentialData is a CBOR-encoded structure (in CDDL notation):
+ * @param out credentialData is a CBOR-encoded structure (in CDDL notation):
*
* CredentialData = [
* tstr, ; docType, an optional name that identifies the type of credential
@@ -230,10 +211,10 @@
* bstr ; credentialPrivKey, the private key for credentialKey
* ]
*
- * @return proofOfProvisioningSignature proves to the IA that the credential was imported into the
- * secure hardware without alteration or error. When the final addEntry() call is made
- * (when the number of provisioned entries equals the sum of the items in
- * startPersonalization() entryCounts parameter), it a COSE_Sign1 structure
+ * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
+ * into the secure hardware without alteration or error. When the final addEntry() call is
+ * made (when the number of provisioned entries equals the sum of the items in
+ * startPersonalization() entryCounts parameter), a COSE_Sign1 structure
* signed by CredentialKey with payload set to the ProofOfProvisioning CBOR below:
*
* ProofOfProvisioning = [
@@ -266,7 +247,6 @@
* "accessControlProfiles" : [ * uint ],
* }
*/
- finishAddingEntries()
- generates(Result result, vec<uint8_t> credentialData,
- vec<uint8_t> proofOfProvisioningSignature);
-};
+ void finishAddingEntries(out byte[] credentialData,
+ out byte[] proofOfProvisioningSignature);
+}
diff --git a/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
new file mode 100644
index 0000000..01d312d
--- /dev/null
+++ b/identity/aidl/android/hardware/identity/SecureAccessControlProfile.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.identity;
+
+import android.hardware.identity.Certificate;
+
+@VintfStability
+parcelable SecureAccessControlProfile {
+ /**
+ * id is a numeric identifier that must be unique within the context of a Credential and may be
+ * used to reference the profile.
+ */
+ int id;
+
+ /**
+ * readerCertificate, if non-empty, specifies a single X.509 certificate (not a chain
+ * of certificates) that must be used to authenticate requests. For details about how
+ * this is done, see the readerSignature paremter of IIdentityCredential.startRetrieval.
+ */
+ Certificate readerCertificate;
+
+ /**
+ * if true, the user is required to authenticate to allow requests. Required authentication
+ * fressness is specified by timeout below.
+ *
+ */
+ boolean userAuthenticationRequired;
+
+ /**
+ * Timeout specifies the amount of time, in milliseconds, for which a user authentication (see
+ * above) is valid, if userAuthenticationRequired is set to true. If userAuthenticationRequired
+ * is true and timout is zero then authentication is required for each reader session.
+ *
+ * If userAuthenticationRequired is false, timeout must be zero.
+ */
+ long timeoutMillis;
+
+ /**
+ * secureUserId must be non-zero if userAuthenticationRequired is true.
+ * It is not related to any Android user ID or UID, but is created in the
+ * Gatekeeper application in the secure environment.
+ */
+ long secureUserId;
+
+ /**
+ * The mac is used to authenticate the access control profile. It contains:
+ *
+ * AES-GCM-ENC(storageKey, R, {}, AccessControlProfile)
+ *
+ * where AccessControlProfile is the CBOR map:
+ *
+ * AccessControlProfile = {
+ * "id": uint,
+ * ? "readerCertificate" : bstr,
+ * ? (
+ * "userAuthenticationRequired" : bool,
+ * "timeoutMillis" : uint,
+ * "secureUserId" : uint
+ * )
+ * }
+ */
+ byte[] mac;
+}
+
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
new file mode 100644
index 0000000..2eb0faa
--- /dev/null
+++ b/identity/aidl/default/Android.bp
@@ -0,0 +1,29 @@
+cc_binary {
+ name: "android.hardware.identity-service.example",
+ relative_install_path: "hw",
+ init_rc: ["identity-default.rc"],
+ vintf_fragments: ["identity-default.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor",
+ "libcrypto",
+ "liblog",
+ "libutils",
+ "android.hardware.identity-support-lib",
+ "android.hardware.identity-ndk_platform",
+ "android.hardware.keymaster-ndk_platform",
+ ],
+ srcs: [
+ "IdentityCredential.cpp",
+ "IdentityCredentialStore.cpp",
+ "WritableIdentityCredential.cpp",
+ "Util.cpp",
+ "service.cpp",
+ ],
+}
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
new file mode 100644
index 0000000..d5b3a0f
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -0,0 +1,768 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::Timestamp;
+using ::std::optional;
+
+using namespace ::android::hardware::identity;
+
+int IdentityCredential::initialize() {
+ auto [item, _, message] = cppbor::parse(credentialData_);
+ if (item == nullptr) {
+ LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Array* arrayItem = item->asArray();
+ if (arrayItem == nullptr || arrayItem->size() != 3) {
+ LOG(ERROR) << "CredentialData is not an array with three elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+ encryptedCredentialKeysItem == nullptr) {
+ LOG(ERROR) << "CredentialData unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ docType_ = docTypeItem->value();
+ testCredential_ = testCredentialItem->value();
+
+ vector<uint8_t> hardwareBoundKey;
+ if (testCredential_) {
+ hardwareBoundKey = support::getTestHardwareBoundKey();
+ } else {
+ hardwareBoundKey = getHardwareBoundKey();
+ }
+
+ const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+ const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> decryptedCredentialKeys =
+ support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+ if (!decryptedCredentialKeys) {
+ LOG(ERROR) << "Error decrypting CredentialKeys";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+ if (dckItem == nullptr) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Array* dckArrayItem = dckItem->asArray();
+ if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
+ LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+ const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+ if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+ LOG(ERROR) << "CredentialKeys unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ storageKey_ = storageKeyItem->value();
+ credentialPrivKey_ = credentialPrivKeyItem->value();
+
+ return IIdentityCredentialStore::STATUS_OK;
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredential(
+ vector<int8_t>* outProofOfDeletionSignature) {
+ cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ vector<uint8_t> proofOfDeletion = array.encode();
+
+ optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
+ proofOfDeletion, // payload
+ {}, // additionalData
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfDeletionSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
+ optional<vector<uint8_t>> kp = support::createEcKeyPair();
+ if (!kp) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
+ }
+
+ // Stash public key of this key-pair for later check in startRetrieval().
+ optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
+ if (!publicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of ephemeral key pair"));
+ }
+ ephemeralPublicKey_ = publicKey.value();
+
+ *outKeyPair = byteStringToSigned(kp.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
+ const vector<int8_t>& publicKey) {
+ readerPublicKey_ = byteStringToUnsigned(publicKey);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
+ uint64_t challenge = 0;
+ while (challenge == 0) {
+ optional<vector<uint8_t>> bytes = support::getRandom(8);
+ if (!bytes) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting random data for challenge"));
+ }
+
+ challenge = 0;
+ for (size_t n = 0; n < bytes.value().size(); n++) {
+ challenge |= ((bytes.value())[n] << (n * 8));
+ }
+ }
+
+ *outChallenge = challenge;
+ return ndk::ScopedAStatus::ok();
+}
+
+// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
+// ahead of time.
+bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& readerCertificateChain) {
+ optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
+ byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
+ if (!acpPubKey) {
+ LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
+ return false;
+ }
+
+ optional<vector<vector<uint8_t>>> certificatesInChain =
+ support::certificateChainSplit(readerCertificateChain);
+ if (!certificatesInChain) {
+ LOG(ERROR) << "Error splitting readerCertificateChain";
+ return false;
+ }
+ for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
+ optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
+ if (!certPubKey) {
+ LOG(ERROR)
+ << "Error extracting public key from certificate in chain presented by reader";
+ return false;
+ }
+ if (acpPubKey.value() == certPubKey.value()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Timestamp clockGetTime() {
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ Timestamp ts;
+ ts.milliSeconds = time.tv_sec * 1000 + time.tv_nsec / 1000000;
+ return ts;
+}
+
+bool checkUserAuthentication(const SecureAccessControlProfile& profile,
+ const HardwareAuthToken& authToken, uint64_t authChallenge) {
+ if (profile.secureUserId != authToken.userId) {
+ LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
+ << ") differs from userId in authToken (" << authToken.userId << ")";
+ return false;
+ }
+
+ if (profile.timeoutMillis == 0) {
+ if (authToken.challenge == 0) {
+ LOG(ERROR) << "No challenge in authToken";
+ return false;
+ }
+
+ if (authToken.challenge != int64_t(authChallenge)) {
+ LOG(ERROR) << "Challenge in authToken doesn't match the challenge we created";
+ return false;
+ }
+ return true;
+ }
+
+ // Note that the Epoch for timestamps in HardwareAuthToken is at the
+ // discretion of the vendor:
+ //
+ // "[...] since some starting point (generally the most recent device
+ // boot) which all of the applications within one secure environment
+ // must agree upon."
+ //
+ // Therefore, if this software implementation is used on a device which isn't
+ // the emulator then the assumption that the epoch is the same as used in
+ // clockGetTime above will not hold. This is OK as this software
+ // implementation should never be used on a real device.
+ //
+ Timestamp now = clockGetTime();
+ if (authToken.timestamp.milliSeconds > now.milliSeconds) {
+ LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
+ << ") is in the future (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
+ LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
+ << profile.timeoutMillis << " = "
+ << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
+ << ") is in the past (now: " << now.milliSeconds << ")";
+ return false;
+ }
+ return true;
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
+ const vector<int8_t>& sessionTranscriptS, const vector<int8_t>& readerSignatureS,
+ const vector<int32_t>& requestCounts) {
+ auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
+ auto itemsRequest = byteStringToUnsigned(itemsRequestS);
+ auto readerSignature = byteStringToUnsigned(readerSignatureS);
+
+ if (sessionTranscript.size() > 0) {
+ auto [item, _, message] = cppbor::parse(sessionTranscript);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "SessionTranscript contains invalid CBOR"));
+ }
+ sessionTranscriptItem_ = std::move(item);
+ }
+ if (numStartRetrievalCalls_ > 0) {
+ if (sessionTranscript_ != sessionTranscript) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
+ "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
+ }
+ }
+ sessionTranscript_ = sessionTranscript;
+
+ // If there is a signature, validate that it was made with the top-most key in the
+ // certificate chain embedded in the COSE_Sign1 structure.
+ optional<vector<uint8_t>> readerCertificateChain;
+ if (readerSignature.size() > 0) {
+ readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+ if (!readerCertificateChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get reader certificate chain from COSE_Sign1"));
+ }
+
+ if (!support::certificateChainValidate(readerCertificateChain.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error validating reader certificate chain"));
+ }
+
+ optional<vector<uint8_t>> readerPublicKey =
+ support::certificateChainGetTopMostKey(readerCertificateChain.value());
+ if (!readerPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get public key from reader certificate chain"));
+ }
+
+ const vector<uint8_t>& itemsRequestBytes = itemsRequest;
+ vector<uint8_t> dataThatWasSigned = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscriptItem_->clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ if (!support::coseCheckEcDsaSignature(readerSignature,
+ dataThatWasSigned, // detached content
+ readerPublicKey.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "readerSignature check failed"));
+ }
+ }
+
+ // Here's where we would validate the passed-in |authToken| to assure ourselves
+ // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+ //
+ // However this involves calculating the MAC. However this requires access
+ // to the key needed to a pre-shared key which we don't have...
+ //
+
+ // To prevent replay-attacks, we check that the public part of the ephemeral
+ // key we previously created, is present in the DeviceEngagement part of
+ // SessionTranscript as a COSE_Key, in uncompressed form.
+ //
+ // We do this by just searching for the X and Y coordinates.
+ if (sessionTranscript.size() > 0) {
+ const cppbor::Array* array = sessionTranscriptItem_->asArray();
+ if (array == nullptr || array->size() != 2) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "SessionTranscript is not an array with two items"));
+ }
+ const cppbor::Semantic* taggedEncodedDE = (*array)[0]->asSemantic();
+ if (taggedEncodedDE == nullptr || taggedEncodedDE->value() != 24) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "First item in SessionTranscript array is not a "
+ "semantic with value 24"));
+ }
+ const cppbor::Bstr* encodedDE = (taggedEncodedDE->child())->asBstr();
+ if (encodedDE == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Child of semantic in first item in SessionTranscript "
+ "array is not a bstr"));
+ }
+ const vector<uint8_t>& bytesDE = encodedDE->value();
+
+ auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+ if (!getXYSuccess) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Error extracting X and Y from ePub"));
+ }
+ if (sessionTranscript.size() > 0 &&
+ !(memmem(bytesDE.data(), bytesDE.size(), ePubX.data(), ePubX.size()) != nullptr &&
+ memmem(bytesDE.data(), bytesDE.size(), ePubY.data(), ePubY.size()) != nullptr)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Did not find ephemeral public key's X and Y coordinates in "
+ "SessionTranscript (make sure leading zeroes are not used)"));
+ }
+ }
+
+ // itemsRequest: If non-empty, contains request data that may be signed by the
+ // reader. The content can be defined in the way appropriate for the
+ // credential, but there are three requirements that must be met to work with
+ // this HAL:
+ if (itemsRequest.size() > 0) {
+ // 1. The content must be a CBOR-encoded structure.
+ auto [item, _, message] = cppbor::parse(itemsRequest);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Error decoding CBOR in itemsRequest"));
+ }
+
+ // 2. The CBOR structure must be a map.
+ const cppbor::Map* map = item->asMap();
+ if (map == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "itemsRequest is not a CBOR map"));
+ }
+
+ // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
+ // the example below.
+ //
+ // NameSpaces = {
+ // + NameSpace => DataElements ; Requested data elements for each NameSpace
+ // }
+ //
+ // NameSpace = tstr
+ //
+ // DataElements = {
+ // + DataElement => IntentToRetain
+ // }
+ //
+ // DataElement = tstr
+ // IntentToRetain = bool
+ //
+ // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
+ // through 3.:
+ //
+ // {
+ // 'docType' : 'org.iso.18013-5.2019',
+ // 'nameSpaces' : {
+ // 'org.iso.18013-5.2019' : {
+ // 'Last name' : false,
+ // 'Birth date' : false,
+ // 'First name' : false,
+ // 'Home address' : true
+ // },
+ // 'org.aamva.iso.18013-5.2019' : {
+ // 'Real Id' : false
+ // }
+ // }
+ // }
+ //
+ const cppbor::Map* nsMap = nullptr;
+ for (size_t n = 0; n < map->size(); n++) {
+ const auto& [keyItem, valueItem] = (*map)[n];
+ if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
+ valueItem->type() == cppbor::MAP) {
+ nsMap = valueItem->asMap();
+ break;
+ }
+ }
+ if (nsMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "No nameSpaces map in top-most map"));
+ }
+
+ for (size_t n = 0; n < nsMap->size(); n++) {
+ auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
+ const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+ const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+ if (nsKey == nullptr || nsInnerMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in nameSpaces map"));
+ }
+ string requestedNamespace = nsKey->value();
+ vector<string> requestedKeys;
+ for (size_t m = 0; m < nsInnerMap->size(); m++) {
+ const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+ const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+ const cppbor::Simple* simple = innerMapValueItem->asSimple();
+ const cppbor::Bool* intentToRetainItem =
+ (simple != nullptr) ? simple->asBool() : nullptr;
+ if (nameItem == nullptr || intentToRetainItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in value in nameSpaces map"));
+ }
+ requestedKeys.push_back(nameItem->value());
+ }
+ requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+ }
+ }
+
+ // Finally, validate all the access control profiles in the requestData.
+ bool haveAuthToken = (authToken.mac.size() > 0);
+ for (const auto& profile : accessControlProfiles) {
+ if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error checking MAC for profile"));
+ }
+ int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
+ if (profile.userAuthenticationRequired) {
+ if (!haveAuthToken || !checkUserAuthentication(profile, authToken, authChallenge_)) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
+ }
+ } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ if (!readerCertificateChain ||
+ !checkReaderAuthentication(profile, readerCertificateChain.value())) {
+ accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
+ }
+ }
+ profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
+ }
+
+ deviceNameSpacesMap_ = cppbor::Map();
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_ = requestCounts;
+ currentNameSpace_ = "";
+
+ itemsRequest_ = itemsRequest;
+
+ numStartRetrievalCalls_ += 1;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) {
+ if (name.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
+ }
+ if (nameSpace.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
+ }
+
+ if (requestCountsRemaining_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more name spaces left to go through"));
+ }
+
+ if (currentNameSpace_ == "") {
+ // First call.
+ currentNameSpace_ = nameSpace;
+ }
+
+ if (nameSpace == currentNameSpace_) {
+ // Same namespace.
+ if (requestCountsRemaining_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more entries to be retrieved in current name space"));
+ }
+ requestCountsRemaining_[0] -= 1;
+ } else {
+ // New namespace.
+ if (requestCountsRemaining_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Moved to new name space but one or more entries need to be retrieved "
+ "in current name space"));
+ }
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+ currentNameSpace_ = nameSpace;
+ }
+
+ // It's permissible to have an empty itemsRequest... but if non-empty you can
+ // only request what was specified in said itemsRequest. Enforce that.
+ if (itemsRequest_.size() > 0) {
+ const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+ if (it == requestedNameSpacesAndNames_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Name space was not requested in startRetrieval"));
+ }
+ const auto& dataItemNames = it->second;
+ if (std::find(dataItemNames.begin(), dataItemNames.end(), name) == dataItemNames.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Data item name in name space was not requested in startRetrieval"));
+ }
+ }
+
+ // Enforce access control.
+ //
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
+ for (auto id : accessControlProfileIds) {
+ auto search = profileIdToAccessCheckResult_.find(id);
+ if (search == profileIdToAccessCheckResult_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Requested entry with unvalidated profile id"));
+ }
+ int accessControlForProfile = search->second;
+ if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
+ accessControl = IIdentityCredentialStore::STATUS_OK;
+ break;
+ }
+ accessControl = accessControlForProfile;
+ }
+ if (accessControl != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(accessControl), "Access control check failed"));
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ currentName_ = name;
+ entryRemainingBytes_ = entrySize;
+ entryValue_.resize(0);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
+ vector<int8_t>* outContent) {
+ auto encryptedContent = byteStringToUnsigned(encryptedContentS);
+
+ optional<vector<uint8_t>> content =
+ support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
+ if (!content) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
+ }
+
+ size_t chunkSize = content.value().size();
+
+ if (chunkSize > entryRemainingBytes_) {
+ LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+ << " is bigger than remaining space of size " << entryRemainingBytes_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved chunk is bigger than remaining space"));
+ }
+
+ entryRemainingBytes_ -= chunkSize;
+ if (entryRemainingBytes_ > 0) {
+ if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
+ }
+ }
+
+ entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
+
+ if (entryRemainingBytes_ == 0) {
+ auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+ if (entryValueItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved data which is invalid CBOR"));
+ }
+ currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
+ }
+
+ *outContent = byteStringToSigned(content.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::finishRetrieval(const vector<int8_t>& signingKeyBlobS,
+ vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) {
+ auto signingKeyBlob = byteStringToUnsigned(signingKeyBlobS);
+
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+ // If there's no signing key or no sessionTranscript or no reader ephemeral
+ // public key, we return the empty MAC.
+ optional<vector<uint8_t>> mac;
+ if (signingKeyBlob.size() > 0 && sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0) {
+ cppbor::Array array;
+ array.add("DeviceAuthentication");
+ array.add(sessionTranscriptItem_->clone());
+ array.add(docType_);
+ array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
+ vector<uint8_t> encodedDeviceAuthentication = array.encode();
+
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> signingKey =
+ support::decryptAes128Gcm(storageKey_, signingKeyBlob, docTypeAsBlob);
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error decrypting signingKeyBlob"));
+ }
+
+ optional<vector<uint8_t>> sharedSecret =
+ support::ecdh(readerPublicKey_, signingKey.value());
+ if (!sharedSecret) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
+ }
+
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ if (!derivedKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error deriving key from shared secret"));
+ }
+
+ mac = support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // additionalData
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
+ }
+ }
+
+ *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
+ *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
+ vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
+ string serialDecimal = "0"; // TODO: set serial to something unique
+ string issuer = "Android Open Source Project";
+ string subject = "Android IdentityCredential Reference Implementation";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
+ if (!signingKeyPKCS8) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingPublicKey =
+ support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
+ if (!signingPublicKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
+ if (!signingKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of signingKey"));
+ }
+
+ optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
+ signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!certificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
+ }
+ vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
+ optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
+ storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
+ if (!encryptedSigningKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
+ }
+ *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
+ *outSigningKeyCertificate = Certificate();
+ outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
new file mode 100644
index 0000000..49ed0d4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredential.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::HardwareAuthToken;
+using ::std::map;
+using ::std::string;
+using ::std::vector;
+
+using MapStringToVectorOfStrings = map<string, vector<string>>;
+
+class IdentityCredential : public BnIdentityCredential {
+ public:
+ IdentityCredential(const vector<uint8_t>& credentialData)
+ : credentialData_(credentialData), numStartRetrievalCalls_(0), authChallenge_(0) {}
+
+ // Parses and decrypts credentialData_, return a status code from
+ // IIdentityCredentialStore. Must be called right after construction.
+ int initialize();
+
+ // Methods from IIdentityCredential follow.
+ ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override;
+ ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override;
+ ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
+ ndk::ScopedAStatus startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
+ const vector<int8_t>& sessionTranscript, const vector<int8_t>& readerSignature,
+ const vector<int32_t>& requestCounts) override;
+ ndk::ScopedAStatus startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) override;
+ ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
+ vector<int8_t>* outContent) override;
+ ndk::ScopedAStatus finishRetrieval(const vector<int8_t>& signingKeyBlob, vector<int8_t>* outMac,
+ vector<int8_t>* outDeviceNameSpaces) override;
+ ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
+ Certificate* outSigningKeyCertificate) override;
+
+ private:
+ // Set by constructor
+ vector<uint8_t> credentialData_;
+ int numStartRetrievalCalls_;
+
+ // Set by initialize()
+ string docType_;
+ bool testCredential_;
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+
+ // Set by createEphemeralKeyPair()
+ vector<uint8_t> ephemeralPublicKey_;
+
+ // Set by setReaderEphemeralPublicKey()
+ vector<uint8_t> readerPublicKey_;
+
+ // Set by createAuthChallenge()
+ uint64_t authChallenge_;
+
+ // Set at startRetrieval() time.
+ map<int32_t, int> profileIdToAccessCheckResult_;
+ vector<uint8_t> sessionTranscript_;
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
+ vector<uint8_t> itemsRequest_;
+ vector<int32_t> requestCountsRemaining_;
+ MapStringToVectorOfStrings requestedNameSpacesAndNames_;
+ cppbor::Map deviceNameSpacesMap_;
+ cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+ // Set at startRetrieveEntryValue() time.
+ string currentNameSpace_;
+ string currentName_;
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryValue_;
+ vector<uint8_t> entryAdditionalData_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/IdentityCredentialStore.cpp
new file mode 100644
index 0000000..1efb4b4
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include <android-base/logging.h>
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
+ HardwareInformation* hardwareInformation) {
+ HardwareInformation hw;
+ hw.credentialStoreName = "Identity Credential Reference Implementation";
+ hw.credentialStoreAuthorName = "Google";
+ hw.dataChunkSize = kGcmChunkSize;
+ hw.isDirectAccess = false;
+ hw.supportedDocTypes = {};
+ *hardwareInformation = hw;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
+ if (!wc->initialize()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::getCredential(
+ CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) {
+ // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
+ if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
+ "Unsupported cipher suite"));
+ }
+
+ vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end());
+ shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data);
+ auto ret = credential->initialize();
+ if (ret != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(ret), "Error initializing IdentityCredential"));
+ }
+ *outCredential = credential;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/IdentityCredentialStore.h
new file mode 100644
index 0000000..a205113
--- /dev/null
+++ b/identity/aidl/default/IdentityCredentialStore.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredentialStore : public BnIdentityCredentialStore {
+ public:
+ IdentityCredentialStore() {}
+
+ // The GCM chunk size used by this implementation is 64 KiB.
+ static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+ // Methods from IIdentityCredentialStore follow.
+ ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
+
+ ndk::ScopedAStatus createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+ ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) override;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
new file mode 100644
index 0000000..a0f86be
--- /dev/null
+++ b/identity/aidl/default/Util.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Util"
+
+#include "Util.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+namespace aidl::android::hardware::identity {
+
+using namespace ::android::hardware::identity;
+
+// This is not a very random HBK but that's OK because this is the SW
+// implementation where it can't be kept secret.
+vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const vector<uint8_t>& getHardwareBoundKey() {
+ return hardwareBoundKey;
+}
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) {
+ return vector<uint8_t>(value.begin(), value.end());
+}
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) {
+ return vector<int8_t>(value.begin(), value.end());
+}
+
+vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
+ cppbor::Map map;
+ map.add("id", profile.id);
+
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ map.add("readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+
+ if (profile.userAuthenticationRequired) {
+ map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ map.add("timeoutMillis", profile.timeoutMillis);
+ map.add("secureUserId", profile.secureUserId);
+ }
+
+ return map.encode();
+}
+
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return {};
+ }
+ optional<vector<uint8_t>> macO =
+ support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
+ if (!macO) {
+ return {};
+ }
+ return macO.value();
+}
+
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey) {
+ vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
+
+ if (profile.mac.size() < support::kAesGcmIvSize) {
+ return false;
+ }
+ vector<uint8_t> nonce =
+ vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
+ optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
+ if (!mac) {
+ return false;
+ }
+ if (mac.value() != byteStringToUnsigned(profile.mac)) {
+ return false;
+ }
+ return true;
+}
+
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds) {
+ cppbor::Map map;
+ map.add("Namespace", nameSpace);
+ map.add("Name", name);
+
+ cppbor::Array acpIds;
+ for (auto id : accessControlProfileIds) {
+ acpIds.add(id);
+ }
+ map.add("AccessControlProfileIds", std::move(acpIds));
+ return map.encode();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
new file mode 100644
index 0000000..ee41ad1
--- /dev/null
+++ b/identity/aidl/default/Util.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_UTIL_H
+#define ANDROID_HARDWARE_IDENTITY_UTIL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor/cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+// Returns the hardware-bound AES-128 key.
+const vector<uint8_t>& getHardwareBoundKey();
+
+// Calculates the MAC for |profile| using |storageKey|.
+optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
+ const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
+
+// Checks authenticity of the MAC in |profile| using |storageKey|.
+bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
+ const vector<uint8_t>& storageKey);
+
+// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
+vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
+ const vector<int32_t> accessControlProfileIds);
+
+vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value);
+
+vector<int8_t> byteStringToSigned(const vector<uint8_t>& value);
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/1.0/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
similarity index 61%
rename from identity/1.0/default/WritableIdentityCredential.cpp
rename to identity/aidl/default/WritableIdentityCredential.cpp
index 4c39f85..ba2062d 100644
--- a/identity/1.0/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -16,9 +16,6 @@
#define LOG_TAG "WritableIdentityCredential"
-#include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android-base/logging.h>
@@ -26,12 +23,315 @@
#include <cppbor/cppbor.h>
#include <cppbor/cppbor_parse.h>
-namespace android {
-namespace hardware {
-namespace identity {
-namespace implementation {
+#include "IdentityCredentialStore.h"
+#include "Util.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
using ::std::optional;
+using namespace ::android::hardware::identity;
+
+bool WritableIdentityCredential::initialize() {
+ optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
+ if (!keyPair) {
+ LOG(ERROR) << "Error creating credentialKey";
+ return false;
+ }
+
+ optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
+ if (!pubKey) {
+ LOG(ERROR) << "Error getting public part of credentialKey";
+ return false;
+ }
+ credentialPubKey_ = pubKey.value();
+
+ optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
+ if (!privKey) {
+ LOG(ERROR) << "Error getting private part of credentialKey";
+ return false;
+ }
+ credentialPrivKey_ = privKey.value();
+
+ optional<vector<uint8_t>> random = support::getRandom(16);
+ if (!random) {
+ LOG(ERROR) << "Error creating storageKey";
+ return false;
+ }
+ storageKey_ = random.value();
+
+ return true;
+}
+
+// TODO: use |attestationApplicationId| and |attestationChallenge| and also
+// ensure the returned certificate chain satisfy the requirements listed in
+// the docs for IWritableIdentityCredential::getAttestationCertificate()
+//
+ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
+ const vector<int8_t>& /*attestationApplicationId*/,
+ const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
+ // For now, we dynamically generate an attestion key on each and every
+ // request and use that to sign CredentialKey. In a real implementation this
+ // would look very differently.
+ optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
+ if (!attestationKeyPair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPubKey =
+ support::ecKeyPairGetPublicKey(attestationKeyPair.value());
+ if (!attestationPubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of attestationKey"));
+ }
+
+ optional<vector<uint8_t>> attestationPrivKey =
+ support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
+ if (!attestationPrivKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting private part of attestationKey"));
+ }
+
+ string serialDecimal;
+ string issuer;
+ string subject;
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+ // First create a certificate for |credentialPubKey| which is signed by
+ // |attestationPrivKey|.
+ //
+ serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential CredentialKey";
+ optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!credentialPubKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for credentialPubKey"));
+ }
+
+ // This is followed by a certificate for |attestationPubKey| self-signed by
+ // |attestationPrivKey|.
+ serialDecimal = "0"; // TODO: set serial
+ issuer = "Android Open Source Project";
+ subject = "Android IdentityCredential AttestationKey";
+ optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
+ attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ if (!attestationKeyCertificate) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating certificate for attestationPubKey"));
+ }
+
+ // Concatenate the certificates to form the chain.
+ vector<uint8_t> certificateChain;
+ certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
+ credentialPubKeyCertificate.value().end());
+ certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
+ attestationKeyCertificate.value().end());
+
+ optional<vector<vector<uint8_t>>> splitCertChain =
+ support::certificateChainSplit(certificateChain);
+ if (!splitCertChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
+ }
+ *outCertificateChain = vector<Certificate>();
+ for (const vector<uint8_t>& cert : splitCertChain.value()) {
+ Certificate c = Certificate();
+ c.encodedCertificate = byteStringToSigned(cert);
+ outCertificateChain->push_back(std::move(c));
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
+ int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+ numAccessControlProfileRemaining_ = accessControlProfileCount;
+ remainingEntryCounts_ = entryCounts;
+ entryNameSpace_ = "";
+
+ signedDataAccessControlProfiles_ = cppbor::Array();
+ signedDataNamespaces_ = cppbor::Map();
+ signedDataCurrentNamespace_ = cppbor::Array();
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) {
+ SecureAccessControlProfile profile;
+
+ if (numAccessControlProfileRemaining_ == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
+ }
+
+ // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
+ // be zero.
+ if (!userAuthenticationRequired && timeoutMillis != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "userAuthenticationRequired is false but timeout is non-zero"));
+ }
+
+ profile.id = id;
+ profile.readerCertificate = readerCertificate;
+ profile.userAuthenticationRequired = userAuthenticationRequired;
+ profile.timeoutMillis = timeoutMillis;
+ profile.secureUserId = secureUserId;
+ optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
+ }
+ profile.mac = byteStringToSigned(mac.value());
+
+ cppbor::Map profileMap;
+ profileMap.add("id", profile.id);
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ profileMap.add(
+ "readerCertificate",
+ cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
+ }
+ if (profile.userAuthenticationRequired) {
+ profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+ profileMap.add("timeoutMillis", profile.timeoutMillis);
+ }
+ signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+ numAccessControlProfileRemaining_--;
+
+ *outSecureAccessControlProfile = profile;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
+ const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
+ int32_t entrySize) {
+ if (numAccessControlProfileRemaining_ != 0) {
+ LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
+ << " and expected zero";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is not zero"));
+ }
+
+ if (remainingEntryCounts_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
+ }
+
+ // Handle initial beginEntry() call.
+ if (entryNameSpace_ == "") {
+ entryNameSpace_ = nameSpace;
+ }
+
+ // If the namespace changed...
+ if (nameSpace != entryNameSpace_) {
+ // Then check that all entries in the previous namespace have been added..
+ if (remainingEntryCounts_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "New namespace but a non-zero number of entries remain to be added"));
+ }
+ remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+ signedDataCurrentNamespace_ = cppbor::Array();
+ }
+ } else {
+ // Same namespace...
+ if (remainingEntryCounts_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Same namespace but no entries remain to be added"));
+ }
+ remainingEntryCounts_[0] -= 1;
+ }
+
+ entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
+
+ entryRemainingBytes_ = entrySize;
+ entryNameSpace_ = nameSpace;
+ entryName_ = name;
+ entryAccessControlProfileIds_ = accessControlProfileIds;
+ entryBytes_.resize(0);
+ // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
+ vector<int8_t>* outEncryptedContent) {
+ auto content = byteStringToUnsigned(contentS);
+ size_t contentSize = content.size();
+
+ if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk of is bigger than kGcmChunkSize"));
+ }
+ if (contentSize > entryRemainingBytes_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk is bigger than remaining space"));
+ }
+
+ entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+ entryRemainingBytes_ -= contentSize;
+ if (entryRemainingBytes_ > 0) {
+ if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk which isn't kGcmChunkSize"));
+ }
+ }
+
+ optional<vector<uint8_t>> nonce = support::getRandom(12);
+ if (!nonce) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
+ }
+ optional<vector<uint8_t>> encryptedContent =
+ support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
+ if (!encryptedContent) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
+ }
+
+ if (entryRemainingBytes_ == 0) {
+ // TODO: ideally do do this without parsing the data (but still validate data is valid
+ // CBOR).
+ auto [item, _, message] = cppbor::parse(entryBytes_);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
+ }
+ cppbor::Map entryMap;
+ entryMap.add("name", entryName_);
+ entryMap.add("value", std::move(item));
+ cppbor::Array profileIdArray;
+ for (auto id : entryAccessControlProfileIds_) {
+ profileIdArray.add(id);
+ }
+ entryMap.add("accessControlProfiles", std::move(profileIdArray));
+ signedDataCurrentNamespace_.add(std::move(entryMap));
+ }
+
+ *outEncryptedContent = byteStringToSigned(encryptedContent.value());
+ return ndk::ScopedAStatus::ok();
+}
// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
// |credentialPrivKey|.
@@ -77,325 +377,8 @@
return true;
}
-bool WritableIdentityCredential::initialize() {
- optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
- if (!keyPair) {
- LOG(ERROR) << "Error creating credentialKey";
- return false;
- }
-
- optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
- if (!pubKey) {
- LOG(ERROR) << "Error getting public part of credentialKey";
- return false;
- }
- credentialPubKey_ = pubKey.value();
-
- optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
- if (!privKey) {
- LOG(ERROR) << "Error getting private part of credentialKey";
- return false;
- }
- credentialPrivKey_ = privKey.value();
-
- optional<vector<uint8_t>> random = support::getRandom(16);
- if (!random) {
- LOG(ERROR) << "Error creating storageKey";
- return false;
- }
- storageKey_ = random.value();
-
- return true;
-}
-
-// TODO: use |attestationApplicationId| and |attestationChallenge| and also
-// ensure the returned certificate chain satisfy the requirements listed in
-// the docs for IWritableIdentityCredential::getAttestationCertificate()
-//
-Return<void> WritableIdentityCredential::getAttestationCertificate(
- const hidl_vec<uint8_t>& /* attestationApplicationId */,
- const hidl_vec<uint8_t>& /* attestationChallenge */,
- getAttestationCertificate_cb _hidl_cb) {
- // For now, we dynamically generate an attestion key on each and every
- // request and use that to sign CredentialKey. In a real implementation this
- // would look very differently.
- optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
- if (!attestationKeyPair) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error creating attestationKey"), {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPubKey =
- support::ecKeyPairGetPublicKey(attestationKeyPair.value());
- if (!attestationPubKey) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting public part of attestationKey"),
- {});
- return Void();
- }
-
- optional<vector<uint8_t>> attestationPrivKey =
- support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
- if (!attestationPrivKey) {
- _hidl_cb(
- support::result(ResultCode::FAILED, "Error getting private part of attestationKey"),
- {});
- return Void();
- }
-
- string serialDecimal;
- string issuer;
- string subject;
- time_t validityNotBefore = time(nullptr);
- time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
- // First create a certificate for |credentialPubKey| which is signed by
- // |attestationPrivKey|.
- //
- serialDecimal = "0"; // TODO: set serial to |attestationChallenge|
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential CredentialKey";
- optional<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
- credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!credentialPubKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for credentialPubKey"),
- {});
- return Void();
- }
-
- // This is followed by a certificate for |attestationPubKey| self-signed by
- // |attestationPrivKey|.
- serialDecimal = "0"; // TODO: set serial
- issuer = "Android Open Source Project";
- subject = "Android IdentityCredential AttestationKey";
- optional<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
- attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
- validityNotBefore, validityNotAfter);
- if (!attestationKeyCertificate) {
- _hidl_cb(support::result(ResultCode::FAILED,
- "Error creating certificate for attestationPubKey"),
- {});
- return Void();
- }
-
- // Concatenate the certificates to form the chain.
- vector<uint8_t> certificateChain;
- certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
- credentialPubKeyCertificate.value().end());
- certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
- attestationKeyCertificate.value().end());
-
- optional<vector<vector<uint8_t>>> splitCertChain =
- support::certificateChainSplit(certificateChain);
- if (!splitCertChain) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error splitting certificate chain"), {});
- return Void();
- }
- hidl_vec<hidl_vec<uint8_t>> ret;
- ret.resize(splitCertChain.value().size());
- std::copy(splitCertChain.value().begin(), splitCertChain.value().end(), ret.begin());
- _hidl_cb(support::resultOK(), ret);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::startPersonalization(uint16_t accessControlProfileCount,
- const hidl_vec<uint16_t>& entryCounts,
- startPersonalization_cb _hidl_cb) {
- numAccessControlProfileRemaining_ = accessControlProfileCount;
- remainingEntryCounts_ = entryCounts;
- entryNameSpace_ = "";
-
- signedDataAccessControlProfiles_ = cppbor::Array();
- signedDataNamespaces_ = cppbor::Map();
- signedDataCurrentNamespace_ = cppbor::Array();
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addAccessControlProfile(
- uint16_t id, const hidl_vec<uint8_t>& readerCertificate, bool userAuthenticationRequired,
- uint64_t timeoutMillis, uint64_t secureUserId, addAccessControlProfile_cb _hidl_cb) {
- SecureAccessControlProfile profile;
-
- if (numAccessControlProfileRemaining_ == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is 0 and expected non-zero"),
- profile);
- return Void();
- }
-
- // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
- // be zero.
- if (!userAuthenticationRequired && timeoutMillis != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "userAuthenticationRequired is false but timeout is non-zero"),
- profile);
- return Void();
- }
-
- profile.id = id;
- profile.readerCertificate = readerCertificate;
- profile.userAuthenticationRequired = userAuthenticationRequired;
- profile.timeoutMillis = timeoutMillis;
- profile.secureUserId = secureUserId;
- optional<vector<uint8_t>> mac =
- support::secureAccessControlProfileCalcMac(profile, storageKey_);
- if (!mac) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error calculating MAC for profile"), profile);
- return Void();
- }
- profile.mac = mac.value();
-
- cppbor::Map profileMap;
- profileMap.add("id", profile.id);
- if (profile.readerCertificate.size() > 0) {
- profileMap.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
- if (profile.userAuthenticationRequired) {
- profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- profileMap.add("timeoutMillis", profile.timeoutMillis);
- }
- signedDataAccessControlProfiles_.add(std::move(profileMap));
-
- numAccessControlProfileRemaining_--;
-
- _hidl_cb(support::resultOK(), profile);
- return Void();
-}
-
-Return<void> WritableIdentityCredential::beginAddEntry(
- const hidl_vec<uint16_t>& accessControlProfileIds, const hidl_string& nameSpace,
- const hidl_string& name, uint32_t entrySize, beginAddEntry_cb _hidl_cb) {
- if (numAccessControlProfileRemaining_ != 0) {
- LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
- << " and expected zero";
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "numAccessControlProfileRemaining_ is %zd and expected zero",
- numAccessControlProfileRemaining_));
- return Void();
- }
-
- if (remainingEntryCounts_.size() == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "No more namespaces to add to"));
- return Void();
- }
-
- // Handle initial beginEntry() call.
- if (entryNameSpace_ == "") {
- entryNameSpace_ = nameSpace;
- }
-
- // If the namespace changed...
- if (nameSpace != entryNameSpace_) {
- // Then check that all entries in the previous namespace have been added..
- if (remainingEntryCounts_[0] != 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "New namespace but %d entries remain to be added",
- int(remainingEntryCounts_[0])));
- return Void();
- }
- remainingEntryCounts_.erase(remainingEntryCounts_.begin());
-
- if (signedDataCurrentNamespace_.size() > 0) {
- signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
- signedDataCurrentNamespace_ = cppbor::Array();
- }
- } else {
- // Same namespace...
- if (remainingEntryCounts_[0] == 0) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Same namespace but no entries remain to be added"));
- return Void();
- }
- remainingEntryCounts_[0] -= 1;
- }
-
- entryAdditionalData_ =
- support::entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
- entryRemainingBytes_ = entrySize;
- entryNameSpace_ = nameSpace;
- entryName_ = name;
- entryAccessControlProfileIds_ = accessControlProfileIds;
- entryBytes_.resize(0);
- // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
-
- _hidl_cb(support::resultOK());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::addEntryValue(const hidl_vec<uint8_t>& content,
- addEntryValue_cb _hidl_cb) {
- size_t contentSize = content.size();
-
- if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(
- ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- if (contentSize > entryRemainingBytes_) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Passed in chunk of size %zd is bigger than remaining space "
- "of size %zd",
- contentSize, entryRemainingBytes_),
- {});
- return Void();
- }
-
- entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
- entryRemainingBytes_ -= contentSize;
- if (entryRemainingBytes_ > 0) {
- if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA,
- "Retrieved non-final chunk of size %zd but expected "
- "kGcmChunkSize which is %zd",
- contentSize, IdentityCredentialStore::kGcmChunkSize),
- {});
- return Void();
- }
- }
-
- optional<vector<uint8_t>> nonce = support::getRandom(12);
- if (!nonce) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error getting nonce"), {});
- return Void();
- }
- optional<vector<uint8_t>> encryptedContent =
- support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
- if (!encryptedContent) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error encrypting content"), {});
- return Void();
- }
-
- if (entryRemainingBytes_ == 0) {
- // TODO: ideally do do this without parsing the data (but still validate data is valid
- // CBOR).
- auto [item, _, message] = cppbor::parse(entryBytes_);
- if (item == nullptr) {
- _hidl_cb(support::result(ResultCode::INVALID_DATA, "Data is not valid CBOR"), {});
- return Void();
- }
- cppbor::Map entryMap;
- entryMap.add("name", entryName_);
- entryMap.add("value", std::move(item));
- cppbor::Array profileIdArray;
- for (auto id : entryAccessControlProfileIds_) {
- profileIdArray.add(id);
- }
- entryMap.add("accessControlProfiles", std::move(profileIdArray));
- signedDataCurrentNamespace_.add(std::move(entryMap));
- }
-
- _hidl_cb(support::resultOK(), encryptedContent.value());
- return Void();
-}
-
-Return<void> WritableIdentityCredential::finishAddingEntries(finishAddingEntries_cb _hidl_cb) {
+ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
+ vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
if (signedDataCurrentNamespace_.size() > 0) {
signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
}
@@ -412,29 +395,27 @@
{}, // additionalData
{}); // certificateChain
if (!signature) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error signing data"), {}, {});
- return Void();
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
}
vector<uint8_t> credentialKeys;
if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialKeys"), {}, {});
- return Void();
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
}
vector<uint8_t> credentialData;
- if (!generateCredentialData(testCredential_ ? support::getTestHardwareBoundKey()
- : support::getHardwareBoundKey(),
- docType_, testCredential_, credentialKeys, credentialData)) {
- _hidl_cb(support::result(ResultCode::FAILED, "Error generating CredentialData"), {}, {});
- return Void();
+ if (!generateCredentialData(
+ testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
+ docType_, testCredential_, credentialKeys, credentialData)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
}
- _hidl_cb(support::resultOK(), credentialData, signature.value());
- return Void();
+ *outCredentialData = byteStringToSigned(credentialData);
+ *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
+ return ndk::ScopedAStatus::ok();
}
-} // namespace implementation
-} // namespace identity
-} // namespace hardware
-} // namespace android
+} // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
new file mode 100644
index 0000000..b380f89
--- /dev/null
+++ b/identity/aidl/default/WritableIdentityCredential.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+
+namespace aidl::android::hardware::identity {
+
+using ::std::string;
+using ::std::vector;
+
+class WritableIdentityCredential : public BnWritableIdentityCredential {
+ public:
+ WritableIdentityCredential(const string& docType, bool testCredential)
+ : docType_(docType), testCredential_(testCredential) {}
+
+ // Creates the Credential Key. Returns false on failure. Must be called
+ // right after construction.
+ bool initialize();
+
+ // Methods from IWritableIdentityCredential follow.
+ ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId,
+ const vector<int8_t>& attestationChallenge,
+ vector<Certificate>* outCertificateChain) override;
+
+ ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
+ const vector<int32_t>& entryCounts) override;
+
+ ndk::ScopedAStatus addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+ int64_t timeoutMillis, int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) override;
+
+ ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ int32_t entrySize) override;
+
+ ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content,
+ vector<int8_t>* outEncryptedContent) override;
+
+ ndk::ScopedAStatus finishAddingEntries(
+ vector<int8_t>* outCredentialData,
+ vector<int8_t>* outProofOfProvisioningSignature) override;
+
+ // private:
+ string docType_;
+ bool testCredential_;
+
+ // These are set in initialize().
+ vector<uint8_t> storageKey_;
+ vector<uint8_t> credentialPrivKey_;
+ vector<uint8_t> credentialPubKey_;
+
+ // These fields are initialized during startPersonalization()
+ size_t numAccessControlProfileRemaining_;
+ vector<int32_t> remainingEntryCounts_;
+ cppbor::Array signedDataAccessControlProfiles_;
+ cppbor::Map signedDataNamespaces_;
+ cppbor::Array signedDataCurrentNamespace_;
+
+ // These fields are initialized during beginAddEntry()
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryAdditionalData_;
+ string entryNameSpace_;
+ string entryName_;
+ vector<int32_t> entryAccessControlProfileIds_;
+ vector<uint8_t> entryBytes_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/identity-default.rc b/identity/aidl/default/identity-default.rc
new file mode 100644
index 0000000..d3b62c1
--- /dev/null
+++ b/identity/aidl/default/identity-default.rc
@@ -0,0 +1,3 @@
+service vendor.identity-default /vendor/bin/hw/android.hardware.identity-service.example
+ class hal
+ user nobody
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
new file mode 100644
index 0000000..a47d354
--- /dev/null
+++ b/identity/aidl/default/identity-default.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.identity</name>
+ <interface>
+ <name>IIdentityCredentialStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
new file mode 100644
index 0000000..f05c615
--- /dev/null
+++ b/identity/aidl/default/service.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.identity-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "IdentityCredentialStore.h"
+
+using aidl::android::hardware::identity::IdentityCredentialStore;
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<IdentityCredentialStore> store =
+ ndk::SharedRefBase::make<IdentityCredentialStore>();
+
+ const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
+ LOG(INFO) << "instance: " << instance;
+ binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
new file mode 100644
index 0000000..21ff440
--- /dev/null
+++ b/identity/aidl/vts/Android.bp
@@ -0,0 +1,21 @@
+cc_test {
+ name: "VtsHalIdentityTargetTest",
+ defaults: [
+ "VtsHalTargetTestDefaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["VtsHalIdentityTargetTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libcppbor",
+ "android.hardware.identity-support-lib",
+ ],
+ static_libs: [
+ "android.hardware.identity-cpp",
+ "android.hardware.keymaster-cpp",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
+}
diff --git a/identity/aidl/vts/VtsHalIdentityTargetTest.cpp b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
new file mode 100644
index 0000000..5abe5a2
--- /dev/null
+++ b/identity/aidl/vts/VtsHalIdentityTargetTest.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "VtsHalIdentityTargetTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+
+namespace android::hardware::identity {
+
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+
+// ---------------------------------------------------------------------------
+// Test Data.
+// ---------------------------------------------------------------------------
+
+struct TestEntryData {
+ TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
+ : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+ TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+ }
+ TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+ vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bstr(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ valueCbor = cppbor::Bool(value).encode();
+ }
+ TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
+ : TestEntryData(nameSpace, name, profileIds) {
+ if (value >= 0) {
+ valueCbor = cppbor::Uint(value).encode();
+ } else {
+ valueCbor = cppbor::Nint(-value).encode();
+ }
+ }
+
+ string nameSpace;
+ string name;
+ vector<uint8_t> valueCbor;
+ vector<int32_t> profileIds;
+};
+
+struct TestProfile {
+ uint16_t id;
+ vector<uint8_t> readerCertificate;
+ bool userAuthenticationRequired;
+ uint64_t timeoutMillis;
+};
+
+// ----------------------------------------------------------------
+
+class IdentityAidl : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+ String16(GetParam().c_str()));
+ ASSERT_NE(credentialStore_, nullptr);
+ }
+
+ sp<IIdentityCredentialStore> credentialStore_;
+};
+
+TEST_P(IdentityAidl, hardwareInformation) {
+ HardwareInformation info;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
+ ASSERT_GT(info.credentialStoreName.size(), 0);
+ ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
+ ASSERT_GE(info.dataChunkSize, 256);
+}
+
+TEST_P(IdentityAidl, createAndRetrieveCredential) {
+ // First, generate a key-pair for the reader since its public key will be
+ // part of the request data.
+ optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+ ASSERT_TRUE(readerKeyPKCS8);
+ optional<vector<uint8_t>> readerPublicKey =
+ support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+ optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+ string serialDecimal = "1234";
+ string issuer = "Android Open Source Project";
+ string subject = "Android IdentityCredential VTS Test";
+ time_t validityNotBefore = time(nullptr);
+ time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+ optional<vector<uint8_t>> readerCertificate = support::ecPublicKeyGenerateCertificate(
+ readerPublicKey.value(), readerKey.value(), serialDecimal, issuer, subject,
+ validityNotBefore, validityNotAfter);
+ ASSERT_TRUE(readerCertificate);
+
+ // Make the portrait image really big (just shy of 256 KiB) to ensure that
+ // the chunking code gets exercised.
+ vector<uint8_t> portraitImage;
+ portraitImage.resize(256 * 1024 - 10);
+ for (size_t n = 0; n < portraitImage.size(); n++) {
+ portraitImage[n] = (uint8_t)n;
+ }
+
+ // Access control profiles:
+ const vector<TestProfile> testProfiles = {// Profile 0 (reader authentication)
+ {0, readerCertificate.value(), false, 0},
+ // Profile 1 (no authentication)
+ {1, {}, false, 0}};
+
+ HardwareAuthToken authToken;
+
+ // Here's the actual test data:
+ const vector<TestEntryData> testEntries = {
+ {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+ {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
+ {"PersonalData", "Home address", string("Maida Vale, London, England"),
+ vector<int32_t>{0}},
+ {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
+ };
+ const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
+ 1u};
+ HardwareInformation hwInfo;
+ ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+ string cborPretty;
+ sp<IWritableIdentityCredential> writableCredential;
+ string docType = "org.iso.18013-5.2019.mdl";
+ bool testCredential = true;
+ ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &writableCredential)
+ .isOk());
+ ASSERT_NE(writableCredential, nullptr);
+
+ string challenge = "attestationChallenge";
+ // TODO: set it to something random and check it's in the cert chain
+ vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
+ vector<Certificate> attestationCertificates;
+ ASSERT_TRUE(writableCredential
+ ->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+ &attestationCertificates)
+ .isOk());
+ ASSERT_GE(attestationCertificates.size(), 2);
+
+ ASSERT_TRUE(
+ writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
+ .isOk());
+
+ vector<SecureAccessControlProfile> returnedSecureProfiles;
+ for (const auto& testProfile : testProfiles) {
+ SecureAccessControlProfile profile;
+ Certificate cert;
+ cert.encodedCertificate = testProfile.readerCertificate;
+ ASSERT_TRUE(writableCredential
+ ->addAccessControlProfile(testProfile.id, cert,
+ testProfile.userAuthenticationRequired,
+ testProfile.timeoutMillis,
+ 0, // secureUserId
+ &profile)
+ .isOk());
+ ASSERT_EQ(testProfile.id, profile.id);
+ ASSERT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+ ASSERT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+ ASSERT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+ ASSERT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+ returnedSecureProfiles.push_back(profile);
+ }
+
+ // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
+ // is a little hacky but it works well enough.
+ map<const TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+
+ for (const auto& entry : testEntries) {
+ vector<vector<uint8_t>> chunks =
+ support::chunkVector(entry.valueCbor, hwInfo.dataChunkSize);
+
+ ASSERT_TRUE(writableCredential
+ ->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+ entry.valueCbor.size())
+ .isOk());
+
+ vector<vector<uint8_t>> encryptedChunks;
+ for (const auto& chunk : chunks) {
+ vector<uint8_t> encryptedChunk;
+ ASSERT_TRUE(writableCredential->addEntryValue(chunk, &encryptedChunk).isOk());
+ encryptedChunks.push_back(encryptedChunk);
+ }
+ encryptedBlobs[&entry] = encryptedChunks;
+ }
+
+ vector<uint8_t> credentialData;
+ vector<uint8_t> proofOfProvisioningSignature;
+ ASSERT_TRUE(
+ writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
+ .isOk());
+
+ optional<vector<uint8_t>> proofOfProvisioning =
+ support::coseSignGetPayload(proofOfProvisioningSignature);
+ ASSERT_TRUE(proofOfProvisioning);
+ cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+ EXPECT_EQ(
+ "[\n"
+ " 'ProofOfProvisioning',\n"
+ " 'org.iso.18013-5.2019.mdl',\n"
+ " [\n"
+ " {\n"
+ " 'id' : 0,\n"
+ " 'readerCertificate' : <not printed>,\n"
+ " },\n"
+ " {\n"
+ " 'id' : 1,\n"
+ " },\n"
+ " ],\n"
+ " {\n"
+ " 'PersonalData' : [\n"
+ " {\n"
+ " 'name' : 'Last name',\n"
+ " 'value' : 'Turing',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Birth date',\n"
+ " 'value' : '19120623',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'First name',\n"
+ " 'value' : 'Alan',\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " {\n"
+ " 'name' : 'Home address',\n"
+ " 'value' : 'Maida Vale, London, England',\n"
+ " 'accessControlProfiles' : [0, ],\n"
+ " },\n"
+ " ],\n"
+ " 'Image' : [\n"
+ " {\n"
+ " 'name' : 'Portrait image',\n"
+ " 'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " 'accessControlProfiles' : [0, 1, ],\n"
+ " },\n"
+ " ],\n"
+ " },\n"
+ " true,\n"
+ "]",
+ cborPretty);
+
+ optional<vector<uint8_t>> credentialPubKey =
+ support::certificateChainGetTopMostKey(attestationCertificates[0].encodedCertificate);
+ ASSERT_TRUE(credentialPubKey);
+ EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+ {}, // Additional data
+ credentialPubKey.value()));
+ writableCredential = nullptr;
+
+ // Now that the credential has been provisioned, read it back and check the
+ // correct data is returned.
+ sp<IIdentityCredential> credential;
+ ASSERT_TRUE(credentialStore_
+ ->getCredential(
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+ credentialData, &credential)
+ .isOk());
+ ASSERT_NE(credential, nullptr);
+
+ optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
+ ASSERT_TRUE(readerEphemeralKeyPair);
+ optional<vector<uint8_t>> readerEphemeralPublicKey =
+ support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
+ ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
+
+ vector<uint8_t> ephemeralKeyPair;
+ ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
+ optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
+
+ // Calculate requestData field and sign it with the reader key.
+ auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
+ ASSERT_TRUE(getXYSuccess);
+ cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+ vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+ vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+ cppbor::Array sessionTranscript = cppbor::Array()
+ .add(cppbor::Semantic(24, deviceEngagementBytes))
+ .add(cppbor::Semantic(24, eReaderPubBytes));
+ vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
+
+ vector<uint8_t> itemsRequestBytes =
+ cppbor::Map("nameSpaces",
+ cppbor::Map()
+ .add("PersonalData", cppbor::Map()
+ .add("Last name", false)
+ .add("Birth date", false)
+ .add("First name", false)
+ .add("Home address", true))
+ .add("Image", cppbor::Map().add("Portrait image", false)))
+ .encode();
+ cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+ EXPECT_EQ(
+ "{\n"
+ " 'nameSpaces' : {\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : false,\n"
+ " 'Birth date' : false,\n"
+ " 'First name' : false,\n"
+ " 'Home address' : true,\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : false,\n"
+ " },\n"
+ " },\n"
+ "}",
+ cborPretty);
+ vector<uint8_t> dataToSign = cppbor::Array()
+ .add("ReaderAuthentication")
+ .add(sessionTranscript.clone())
+ .add(cppbor::Semantic(24, itemsRequestBytes))
+ .encode();
+ optional<vector<uint8_t>> readerSignature =
+ support::coseSignEcDsa(readerKey.value(), {}, // content
+ dataToSign, // detached content
+ readerCertificate.value());
+ ASSERT_TRUE(readerSignature);
+
+ ASSERT_TRUE(credential
+ ->startRetrieval(returnedSecureProfiles, authToken, itemsRequestBytes,
+ sessionTranscriptBytes, readerSignature.value(),
+ testEntriesEntryCounts)
+ .isOk());
+
+ for (const auto& entry : testEntries) {
+ ASSERT_TRUE(credential
+ ->startRetrieveEntryValue(entry.nameSpace, entry.name,
+ entry.valueCbor.size(), entry.profileIds)
+ .isOk());
+
+ auto it = encryptedBlobs.find(&entry);
+ ASSERT_NE(it, encryptedBlobs.end());
+ const vector<vector<uint8_t>>& encryptedChunks = it->second;
+
+ vector<uint8_t> content;
+ for (const auto& encryptedChunk : encryptedChunks) {
+ vector<uint8_t> chunk;
+ ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
+ content.insert(content.end(), chunk.begin(), chunk.end());
+ }
+ EXPECT_EQ(content, entry.valueCbor);
+ }
+
+ // Generate the key that will be used to sign AuthenticatedData.
+ vector<uint8_t> signingKeyBlob;
+ Certificate signingKeyCertificate;
+ ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+
+ vector<uint8_t> mac;
+ vector<uint8_t> deviceNameSpacesBytes;
+ ASSERT_TRUE(credential->finishRetrieval(signingKeyBlob, &mac, &deviceNameSpacesBytes).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_EQ(
+ "{\n"
+ " 'PersonalData' : {\n"
+ " 'Last name' : 'Turing',\n"
+ " 'Birth date' : '19120623',\n"
+ " 'First name' : 'Alan',\n"
+ " 'Home address' : 'Maida Vale, London, England',\n"
+ " },\n"
+ " 'Image' : {\n"
+ " 'Portrait image' : <bstr size=262134 "
+ "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+ " },\n"
+ "}",
+ cborPretty);
+ // The data that is MACed is ["DeviceAuthentication", sessionTranscriptBytes, docType,
+ // deviceNameSpacesBytes] so build up that structure
+ cppbor::Array deviceAuthentication;
+ deviceAuthentication.add("DeviceAuthentication");
+ deviceAuthentication.add(sessionTranscript.clone());
+ deviceAuthentication.add(docType);
+ deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
+ vector<uint8_t> encodedDeviceAuthentication = deviceAuthentication.encode();
+ optional<vector<uint8_t>> signingPublicKey =
+ support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+ EXPECT_TRUE(signingPublicKey);
+
+ // Derive the key used for MACing.
+ optional<vector<uint8_t>> readerEphemeralPrivateKey =
+ support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
+ optional<vector<uint8_t>> sharedSecret =
+ support::ecdh(signingPublicKey.value(), readerEphemeralPrivateKey.value());
+ ASSERT_TRUE(sharedSecret);
+ vector<uint8_t> salt = {0x00};
+ vector<uint8_t> info = {};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ ASSERT_TRUE(derivedKey);
+ optional<vector<uint8_t>> calculatedMac =
+ support::coseMac0(derivedKey.value(), {}, // payload
+ encodedDeviceAuthentication); // detached content
+ ASSERT_TRUE(calculatedMac);
+ EXPECT_EQ(mac, calculatedMac);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Identity, IdentityAidl,
+ testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+ android::PrintInstanceNameToString);
+// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
+// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
+
+} // namespace android::hardware::identity
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ ::android::ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 38dc10b..7b4546b 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -23,7 +23,6 @@
"include",
],
shared_libs: [
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
@@ -41,7 +40,6 @@
],
shared_libs: [
"android.hardware.identity-support-lib",
- "android.hardware.identity@1.0",
"libcrypto",
"libbase",
"libhidlbase",
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 485571a..4533ad9 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -18,12 +18,11 @@
#define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
#include <cstdint>
+#include <optional>
#include <string>
#include <tuple>
#include <vector>
-#include <android/hardware/identity/1.0/types.h>
-
namespace android {
namespace hardware {
namespace identity {
@@ -34,10 +33,6 @@
using ::std::tuple;
using ::std::vector;
-using ::android::hardware::identity::V1_0::Result;
-using ::android::hardware::identity::V1_0::ResultCode;
-using ::android::hardware::identity::V1_0::SecureAccessControlProfile;
-
// ---------------------------------------------------------------------------
// Miscellaneous utilities.
// ---------------------------------------------------------------------------
@@ -258,21 +253,11 @@
const vector<uint8_t>& detachedContent);
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-// Returns a reference to a Result with code OK and empty message.
-const Result& resultOK();
-
-// Returns a new Result with the given code and message.
-Result result(ResultCode code, const char* format, ...) __attribute__((format(printf, 2, 3)));
+// Returns the testing AES-128 key where all bits are set to 0.
+const vector<uint8_t>& getTestHardwareBoundKey();
// Splits the given bytestring into chunks. If the given vector is smaller or equal to
// |maxChunkSize| a vector with |content| as the only element is returned. Otherwise
@@ -280,21 +265,6 @@
// may be smaller than |maxChunkSize|.
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize);
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey);
-
-// Returns the testing AES-128 key where all bits are set to 0.
-const vector<uint8_t>& getTestHardwareBoundKey();
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds);
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 7d93a4b..e2828bf 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -1682,36 +1682,9 @@
}
// ---------------------------------------------------------------------------
-// Platform abstraction.
-// ---------------------------------------------------------------------------
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
- return hardwareBoundKey;
-}
-
-// ---------------------------------------------------------------------------
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
-Result okResult{ResultCode::OK, ""};
-
-const Result& resultOK() {
- return okResult;
-}
-
-Result result(ResultCode code, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- string str;
- android::base::StringAppendV(&str, format, ap);
- va_end(ap);
- return Result{code, str};
-}
-
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
vector<vector<uint8_t>> ret;
@@ -1738,56 +1711,6 @@
return ret;
}
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
- cppbor::Map map;
- map.add("id", profile.id);
-
- if (profile.readerCertificate.size() > 0) {
- map.add("readerCertificate", cppbor::Bstr(profile.readerCertificate));
- }
-
- if (profile.userAuthenticationRequired) {
- map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
- map.add("timeoutMillis", profile.timeoutMillis);
- map.add("secureUserId", profile.secureUserId);
- }
-
- return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
- const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- optional<vector<uint8_t>> nonce = getRandom(12);
- if (!nonce) {
- return {};
- }
- optional<vector<uint8_t>> macO = encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
- if (!macO) {
- return {};
- }
- return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
- const vector<uint8_t>& storageKey) {
- vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
- if (profile.mac.size() < kAesGcmIvSize) {
- return false;
- }
- vector<uint8_t> nonce =
- vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + kAesGcmIvSize);
- optional<vector<uint8_t>> mac = encryptAes128Gcm(storageKey, nonce, {}, cborData);
- if (!mac) {
- return false;
- }
- if (mac.value() != vector<uint8_t>(profile.mac)) {
- return false;
- }
- return true;
-}
vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -1795,20 +1718,6 @@
return testHardwareBoundKey;
}
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
- const vector<uint16_t> accessControlProfileIds) {
- cppbor::Map map;
- map.add("Namespace", nameSpace);
- map.add("Name", name);
-
- cppbor::Array acpIds;
- for (auto id : accessControlProfileIds) {
- acpIds.add(id);
- }
- map.add("AccessControlProfileIds", std::move(acpIds));
- return map.encode();
-}
-
} // namespace support
} // namespace identity
} // namespace hardware
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
new file mode 100644
index 0000000..a2d73ead
--- /dev/null
+++ b/keymaster/aidl/Android.bp
@@ -0,0 +1,18 @@
+aidl_interface {
+ name: "android.hardware.keymaster",
+ vendor_available: true,
+ srcs: [
+ "android/hardware/keymaster/*.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ java: {
+ platform_apis: true,
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
new file mode 100644
index 0000000..58602aa
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.keymaster;
+
+import android.hardware.keymaster.Timestamp;
+import android.hardware.keymaster.HardwareAuthenticatorType;
+
+/**
+ * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
+ *
+ * HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and
+ * Fingerprint, in response to successful user authentication events. These tokens are passed to
+ * begin(), update(), and finish() to prove that authentication occurred. See those methods for
+ * more details. It is up to the caller to determine which of the generated auth tokens is
+ * appropriate for a given key operation.
+ */
+@VintfStability
+parcelable HardwareAuthToken {
+
+ /**
+ * challenge is a value that's used to enable authentication tokens to authorize specific
+ * events. The primary use case for challenge is to authorize an IKeymasterDevice cryptographic
+ * operation, for keys that require authentication per operation. See begin() for details.
+ */
+ long challenge;
+
+ /**
+ * userId is the a "secure" user ID. It is not related to any Android user ID or UID, but is
+ * created in the Gatekeeper application in the secure environment.
+ */
+ long userId;
+
+ /**
+ * authenticatorId is the a "secure" user ID. It is not related to any Android user ID or UID,
+ * but is created in an authentication application in the secure environment, such as the
+ * Fingerprint application.
+ */
+ long authenticatorId; // Secure authenticator ID.
+
+ /**
+ * authenticatorType describes the type of authentication that took place, e.g. password or
+ * fingerprint.
+ */
+ HardwareAuthenticatorType authenticatorType;
+
+ /**
+ * timestamp indicates when the user authentication took place, in milliseconds since some
+ * starting point (generally the most recent device boot) which all of the applications within
+ * one secure environment must agree upon. This timestamp is used to determine whether or not
+ * the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT).
+ */
+ Timestamp timestamp;
+
+ /**
+ * MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0
+ * and Fingerprint 1.0, as well as pre-treble HALs.
+ *
+ * The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows:
+ *
+ * HMAC_SHA256(
+ * H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp)
+ *
+ * where ``||'' represents concatenation, the leading zero is a single byte, and all integers
+ * are represented as unsigned values, the full width of the type. The challenge, userId and
+ * authenticatorId values are in machine order, but authenticatorType and timestamp are in
+ * network order (big-endian). This odd construction is compatible with the hw_auth_token_t
+ * structure,
+ *
+ * Note that mac is a vec rather than an array, not because it's actually variable-length but
+ * because it could be empty. As documented in the IKeymasterDevice::begin,
+ * IKeymasterDevice::update and IKeymasterDevice::finish doc comments, an empty mac indicates
+ * that this auth token is empty.
+ */
+ byte[] mac;
+}
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..3141858
--- /dev/null
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthenticatorType.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.keymaster;
+
+/**
+ * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
+ * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
+ * authenticating to activate a key.
+ */
+@VintfStability
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+ NONE = 0,
+ PASSWORD = 1 << 0,
+ FINGERPRINT = 1 << 1,
+ // Additional entries must be powers of 2.
+ ANY = 0xFFFFFFFF,
+}
diff --git a/radio/config/1.3/IRadioConfigIndication.hal b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
similarity index 66%
rename from radio/config/1.3/IRadioConfigIndication.hal
rename to keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 9ef496c..4b2f108 100644
--- a/radio/config/1.3/IRadioConfigIndication.hal
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-package android.hardware.radio.config@1.3;
+package android.hardware.keymaster;
-import @1.2::IRadioConfigIndication;
-
-/**
- * Interface declaring unsolicited radio config indications.
- */
-interface IRadioConfigIndication extends @1.2::IRadioConfigIndication {
-
-};
+@VintfStability
+parcelable Timestamp {
+ long milliSeconds;
+}
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index 79f9c32..e0b04a8 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -48,19 +48,6 @@
getCapabilities_1_3() generates (ErrorStatus status, Capabilities capabilities);
/**
- * Returns whether the device is able to complete or abort a task within a
- * specified duration.
- *
- * @return prepareModelDeadline 'true' if the device supports completing or
- * aborting model preparation by the deadline when the deadline is supplied,
- * 'false' otherwise.
- * @return executionDeadline 'true' if the device supports completing or
- * aborting an execution by the deadline when the deadline is supplied,
- * 'false' otherwise.
- */
- supportsDeadlines() generates (bool prepareModelDeadline, bool executionDeadline);
-
- /**
* Gets the supported operations in a model.
*
* getSupportedOperations indicates which operations of the top-level
@@ -140,14 +127,10 @@
*
* prepareModel_1_3 can be called with an optional deadline. If the model
* is not able to be prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support preparation deadlines via
- * IDevice::supportsDeadlines, and prepareModel_1_3 is called with a
- * deadline, then the argument is invalid, and {@link
- * ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Optionally, the driver may save the prepared model to cache during the
* asynchronous preparation. Any error that occurs when saving to cache must
@@ -172,9 +155,9 @@
* model.
* @param priority The priority of the prepared model relative to other
* prepared models owned by the client.
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must either be 0 indicating that caching information is
@@ -209,8 +192,8 @@
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments related to preparing
* the model is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModel_1_3(Model model, ExecutionPreference preference,
@@ -262,14 +245,11 @@
*
* prepareModelFromCache_1_3 can be called with an optional deadline. If the
* model is not able to prepared before the provided deadline, the model
- * preparation must be aborted, and either {@link
+ * preparation may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT}
- * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The
+ * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
* error due to an abort must be sent the same way as other errors,
- * described above. If the service reports that it does not support
- * preparation deadlines via IDevice::supportsDeadlines, and
- * prepareModelFromCache_1_3 is called with a deadline, then the argument is
- * invalid, and {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
+ * described above.
*
* The only information that may be unknown to the model at this stage is
* the shape of the tensors, which may only be known at execution time. As
@@ -279,9 +259,9 @@
* used with different shapes of inputs on different (possibly concurrent)
* executions.
*
- * @param deadline The time by which the model must be prepared. If the
- * model cannot be prepared by the deadline, the preparation must be
- * aborted.
+ * @param deadline The time by which the model is expected to be prepared.
+ * If the model cannot be prepared by the deadline, the preparation may
+ * be aborted.
* @param modelCache A vector of handles with each entry holding exactly one
* cache file descriptor for the security-sensitive cache. The length of
* the vector must match the numModelCache returned from getNumberOfCacheFilesNeeded.
@@ -307,8 +287,8 @@
* - GENERAL_FAILURE if caching is not supported or if there is an
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid
- * - MISSED_DEADLINE_* if the deadline for preparing a model cannot be
- * met
+ * - MISSED_DEADLINE_* if the preparation is aborted because the model
+ * cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
*/
prepareModelFromCache_1_3(OptionalTimePoint deadline,
diff --git a/neuralnetworks/1.3/IExecutionCallback.hal b/neuralnetworks/1.3/IExecutionCallback.hal
index 439428a..ea11b17 100644
--- a/neuralnetworks/1.3/IExecutionCallback.hal
+++ b/neuralnetworks/1.3/IExecutionCallback.hal
@@ -47,7 +47,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline could not be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
* @param outputShapes A list of shape information of model output operands.
* The index into "outputShapes" corresponds with to index
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
index 6030809..949438e 100644
--- a/neuralnetworks/1.3/IFencedExecutionCallback.hal
+++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal
@@ -38,8 +38,8 @@
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if the asynchronous task resulted in an
* unspecified error
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return timingLaunched The duration starts when executeFenced is called and ends when
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index 4ce3691..a1814b5 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -70,14 +70,10 @@
*
* execute_1_3 can be called with an optional deadline. If the execution
* is not able to be completed before the provided deadline, the execution
- * must be aborted, and either {@link
+ * may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and execute_1_3 is called with a deadline,
- * then the argument is invalid, and {@link ErrorStatus::INVALID_ARGUMENT}
- * must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -89,9 +85,9 @@
* The duration runs from the time the driver sees the call
* to the execute_1_3 function to the time the driver invokes
* the callback.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be completed by the deadline, the
+ * execution may be aborted.
* @param loopTimeoutDuration The maximum amount of time that should be spent
* executing a {@link OperationType::WHILE}
* operation. If a loop condition model does not
@@ -116,8 +112,8 @@
* not large enough to store the resultant values
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
*/
@@ -150,16 +146,12 @@
* (ErrorStatus::NONE): There must be no failure unless the device itself is
* in a bad state.
*
- * executeSynchronously_1_3 can be called with an optional deadline. If the
+ * executeSynchronously_1_3 may be called with an optional deadline. If the
* execution is not able to be completed before the provided deadline, the
- * execution must be aborted, and either {@link
+ * execution may be aborted, and either {@link
* ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and executeSynchronously_1_3 is called with a
- * deadline, then the argument is invalid, and
- * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* Any number of calls to the execute* and executeSynchronously* functions,
* in any combination, may be made concurrently, even on the same
@@ -171,9 +163,9 @@
* The duration runs from the time the driver sees the call
* to the executeSynchronously_1_3 function to the time the driver
* returns from the function.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
* @param loopTimeoutDuration The maximum amount of time that should be spent
* executing a {@link OperationType::WHILE}
* operation. If a loop condition model does not
@@ -194,8 +186,8 @@
* corresponding output
* - INVALID_ARGUMENT if one of the input arguments is
* invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return outputShapes A list of shape information of model output operands.
@@ -236,17 +228,13 @@
* any data object referenced by 'request' (described by the
* {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
*
- * executeFenced can be called with an optional deadline and an optional duration.
+ * executeFenced may be called with an optional deadline and an optional duration.
* If the execution is not able to be completed before the provided deadline or
* within the timeout duration (measured from when all sync fences in waitFor are
- * signaled), whichever comes earlier, the execution must be aborted, and either
+ * signaled), whichever comes earlier, the execution may be aborted, and either
* {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
- * ErrorStatus::MISSED_DEADLINE_PERSISTENT} must be returned. The error due
+ * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
* to an abort must be sent the same way as other errors, described above.
- * If the service reports that it does not support execution deadlines via
- * IDevice::supportsDeadlines, and executeFenced is called with a
- * deadline or duration, then the argument is invalid, and
- * {@link ErrorStatus::INVALID_ARGUMENT} must be returned.
*
* If any of the sync fences in waitFor changes to error status after the executeFenced
* call succeeds, or the execution is aborted because it cannot finish before the deadline
@@ -263,9 +251,9 @@
* @param waitFor A vector of sync fence file descriptors.
* Execution must not start until all sync fences have been signaled.
* @param measure Specifies whether or not to measure duration of the execution.
- * @param deadline The time by which the execution must complete. If the
- * execution cannot be finished by the deadline, the
- * execution must be aborted.
+ * @param deadline The time by which the execution is expected to complete.
+ * If the execution cannot be finished by the deadline, the
+ * execution may be aborted.
* @param loopTimeoutDuration The maximum amount of time that should be spent
* executing a {@link OperationType::WHILE}
* operation. If a loop condition model does not
@@ -277,18 +265,18 @@
* LoopTimeoutDurationNs::DEFAULT}. When
* provided, the duration must not exceed {@link
* LoopTimeoutDurationNs::MAXIMUM}.
- * @param duration The length of time within which the execution must
- * complete after all sync fences in waitFor are signaled. If the
- * execution cannot be finished within the duration, the execution
- * must be aborted.
+ * @param duration The length of time within which the execution is expected
+ * to complete after all sync fences in waitFor are signaled.
+ * If the execution cannot be finished within the duration,
+ * the execution may be aborted.
* @return status Error status of the call, must be:
* - NONE if task is successfully launched
* - DEVICE_UNAVAILABLE if driver is offline or busy
* - GENERAL_FAILURE if there is an unspecified error
* - INVALID_ARGUMENT if one of the input arguments is invalid, including
* fences in error states.
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the execution is aborted because it
+ * cannot be completed by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @return syncFence The sync fence that will be signaled when the task is completed.
diff --git a/neuralnetworks/1.3/IPreparedModelCallback.hal b/neuralnetworks/1.3/IPreparedModelCallback.hal
index 11ebbf4..c0d3416 100644
--- a/neuralnetworks/1.3/IPreparedModelCallback.hal
+++ b/neuralnetworks/1.3/IPreparedModelCallback.hal
@@ -47,8 +47,8 @@
* unspecified error
* - INVALID_ARGUMENT if one of the input arguments to
* prepareModel is invalid
- * - MISSED_DEADLINE_* if the deadline for executing a model
- * cannot be met
+ * - MISSED_DEADLINE_* if the preparation is aborted because
+ * the model cannot be prepared by the deadline
* - RESOURCE_EXHAUSTED_* if the task was aborted by the
* driver
* @param preparedModel A model that has been asynchronously prepared for
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index b04abe2..8c9393b 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -858,12 +858,6 @@
void GeneratedTestBase::SetUp() {
testing::TestWithParam<GeneratedTestParam>::SetUp();
ASSERT_NE(kDevice, nullptr);
-
- const Return<void> ret =
- kDevice->supportsDeadlines([this](bool prepareModelDeadline, bool executionDeadline) {
- mSupportsDeadlines = {prepareModelDeadline, executionDeadline};
- });
- ASSERT_TRUE(ret.isOk());
}
std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index a8db515..834d335 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -36,7 +36,6 @@
void SetUp() override;
const sp<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
- std::pair<bool, bool> mSupportsDeadlines;
};
using FilterFn = std::function<bool(const test_helper::TestModel&)>;
diff --git a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
index fccc612..879989e 100644
--- a/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/QualityOfServiceTests.cpp
@@ -34,45 +34,52 @@
using HidlToken =
hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
-enum class DeadlineBoundType { NOW, UNLIMITED };
-constexpr std::array<DeadlineBoundType, 2> deadlineBounds = {DeadlineBoundType::NOW,
- DeadlineBoundType::UNLIMITED};
+enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
+constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
+ DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
std::string toString(DeadlineBoundType type) {
switch (type) {
case DeadlineBoundType::NOW:
return "NOW";
case DeadlineBoundType::UNLIMITED:
return "UNLIMITED";
+ case DeadlineBoundType::SHORT:
+ return "SHORT";
}
LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
return {};
}
+constexpr auto kShortDuration = std::chrono::milliseconds{5};
+
using Results = std::tuple<ErrorStatus, hidl_vec<OutputShape>, Timing>;
using MaybeResults = std::optional<Results>;
using ExecutionFunction =
std::function<MaybeResults(const sp<IPreparedModel>& preparedModel, const Request& request,
- DeadlineBoundType deadlineBound)>;
+ const OptionalTimePoint& deadline)>;
-static OptionalTimePoint makeOptionalTimePoint(DeadlineBoundType deadlineBoundType) {
- OptionalTimePoint deadline;
+static OptionalTimePoint makeDeadline(DeadlineBoundType deadlineBoundType) {
+ const auto getNanosecondsSinceEpoch = [](const auto& time) -> uint64_t {
+ const auto timeSinceEpoch = time.time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
+ };
+
+ std::chrono::steady_clock::time_point timePoint;
switch (deadlineBoundType) {
- case DeadlineBoundType::NOW: {
- const auto currentTime = std::chrono::steady_clock::now();
- const auto currentTimeInNanoseconds =
- std::chrono::time_point_cast<std::chrono::nanoseconds>(currentTime);
- const uint64_t nanosecondsSinceEpoch =
- currentTimeInNanoseconds.time_since_epoch().count();
- deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
- } break;
- case DeadlineBoundType::UNLIMITED: {
- const auto maxTime = std::chrono::time_point<std::chrono::steady_clock,
- std::chrono::nanoseconds>::max();
- const uint64_t nanosecondsSinceEpoch = maxTime.time_since_epoch().count();
- deadline.nanosecondsSinceEpoch(nanosecondsSinceEpoch);
- } break;
+ case DeadlineBoundType::NOW:
+ timePoint = std::chrono::steady_clock::now();
+ break;
+ case DeadlineBoundType::UNLIMITED:
+ timePoint = std::chrono::steady_clock::time_point::max();
+ break;
+ case DeadlineBoundType::SHORT:
+ timePoint = std::chrono::steady_clock::now() + kShortDuration;
+ break;
}
+
+ OptionalTimePoint deadline;
+ deadline.nanosecondsSinceEpoch(getNanosecondsSinceEpoch(timePoint));
return deadline;
}
@@ -80,7 +87,7 @@
std::optional<DeadlineBoundType> deadlineBound) {
OptionalTimePoint deadline;
if (deadlineBound.has_value()) {
- deadline = makeOptionalTimePoint(deadlineBound.value());
+ deadline = makeDeadline(deadlineBound.value());
}
// see if service can handle model
@@ -127,11 +134,11 @@
} else {
switch (deadlineBound.value()) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- EXPECT_TRUE(prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
+ prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
break;
case DeadlineBoundType::UNLIMITED:
@@ -145,8 +152,7 @@
ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
}
-void runPrepareModelTests(const sp<IDevice>& device, const Model& model,
- bool supportsPrepareModelDeadline) {
+void runPrepareModelTests(const sp<IDevice>& device, const Model& model) {
// test priority
for (auto priority : hidl_enum_range<Priority>{}) {
SCOPED_TRACE("priority: " + toString(priority));
@@ -155,19 +161,17 @@
}
// test deadline
- if (supportsPrepareModelDeadline) {
- for (auto deadlineBound : deadlineBounds) {
- SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
- runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
- }
+ for (auto deadlineBound : deadlineBounds) {
+ SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+ runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
}
}
static MaybeResults executeAsynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("asynchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// launch execution
const sp<ExecutionCallback> callback = new ExecutionCallback();
@@ -187,14 +191,17 @@
}
static MaybeResults executeSynchronously(const sp<IPreparedModel>& preparedModel,
- const Request& request, DeadlineBoundType deadlineBound) {
+ const Request& request,
+ const OptionalTimePoint& deadline) {
SCOPED_TRACE("synchronous");
const MeasureTiming measure = MeasureTiming::NO;
- const OptionalTimePoint deadline = makeOptionalTimePoint(deadlineBound);
// configure results callback
MaybeResults results;
- const auto cb = [&results](const auto&... args) { *results = {args...}; };
+ const auto cb = [&results](ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+ const Timing& timing) {
+ results.emplace(status, outputShapes, timing);
+ };
// run execution
const Return<void> ret =
@@ -209,9 +216,10 @@
void runExecutionTest(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
const Request& request, bool synchronous, DeadlineBoundType deadlineBound) {
const ExecutionFunction execute = synchronous ? executeSynchronously : executeAsynchronously;
+ const auto deadline = makeDeadline(deadlineBound);
// Perform execution and unpack results.
- const auto results = execute(preparedModel, request, deadlineBound);
+ const auto results = execute(preparedModel, request, deadline);
if (!results.has_value()) return;
const auto& [status, outputShapes, timing] = results.value();
@@ -222,13 +230,13 @@
// Validate deadline information if applicable.
switch (deadlineBound) {
case DeadlineBoundType::NOW:
- // If the execution was launched with a deadline of NOW, the
- // deadline has already passed when the driver would launch the
- // execution. In this case, the driver must return
- // MISSED_DEADLINE_*.
- ASSERT_TRUE(status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+ case DeadlineBoundType::SHORT:
+ // Either the driver successfully completed the task or it
+ // aborted and returned MISSED_DEADLINE_*.
+ ASSERT_TRUE(status == ErrorStatus::NONE ||
+ status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
- return;
+ break;
case DeadlineBoundType::UNLIMITED:
// If an unlimited deadline is supplied, we expect the execution to
// proceed normally. In this case, check it normally by breaking out
@@ -256,7 +264,9 @@
const std::vector<TestBuffer> outputs = getOutputBuffers(request10);
// We want "close-enough" results.
- checkResults(testModel, outputs);
+ if (status == ErrorStatus::NONE) {
+ checkResults(testModel, outputs);
+ }
}
void runExecutionTests(const sp<IPreparedModel>& preparedModel, const TestModel& testModel,
@@ -268,32 +278,27 @@
}
}
-void runTests(const sp<IDevice>& device, const TestModel& testModel,
- std::pair<bool, bool> supportsDeadlines) {
+void runTests(const sp<IDevice>& device, const TestModel& testModel) {
// setup
- const auto [supportsPrepareModelDeadline, supportsExecutionDeadline] = supportsDeadlines;
- if (!supportsPrepareModelDeadline && !supportsExecutionDeadline) return;
const Model model = createModel(testModel);
// run prepare model tests
- runPrepareModelTests(device, model, supportsPrepareModelDeadline);
+ runPrepareModelTests(device, model);
- if (supportsExecutionDeadline) {
- // prepare model
- sp<IPreparedModel> preparedModel;
- createPreparedModel(device, model, &preparedModel);
- if (preparedModel == nullptr) return;
+ // prepare model
+ sp<IPreparedModel> preparedModel;
+ createPreparedModel(device, model, &preparedModel);
+ if (preparedModel == nullptr) return;
- // run execution tests
- const Request request = nn::convertToV1_3(createRequest(testModel));
- runExecutionTests(preparedModel, testModel, request);
- }
+ // run execution tests
+ const Request request = nn::convertToV1_3(createRequest(testModel));
+ runExecutionTests(preparedModel, testModel, request);
}
class DeadlineTest : public GeneratedTestBase {};
TEST_P(DeadlineTest, Test) {
- runTests(kDevice, kTestModel, mSupportsDeadlines);
+ runTests(kDevice, kTestModel);
}
INSTANTIATE_GENERATED_TEST(DeadlineTest,
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 09e9922..8f2d4b7 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -44,18 +44,12 @@
}
static void validatePrepareModel(const sp<IDevice>& device, const std::string& message,
- const Model& model, ExecutionPreference preference,
- bool testDeadline) {
+ const Model& model, ExecutionPreference preference) {
SCOPED_TRACE(message + " [prepareModel_1_3]");
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanosecondsSinceEpoch(std::numeric_limits<uint64_t>::max());
- }
-
sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_3(
- model, preference, kDefaultPriority, deadline, hidl_vec<hidl_handle>(),
+ model, preference, kDefaultPriority, {}, hidl_vec<hidl_handle>(),
hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
ASSERT_TRUE(prepareLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(prepareLaunchStatus));
@@ -79,13 +73,12 @@
// to the model does not leave this function.
static void validate(const sp<IDevice>& device, const std::string& message, Model model,
const std::function<void(Model*)>& mutation,
- ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER,
- bool testDeadline = false) {
+ ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER) {
mutation(&model);
- if (validExecutionPreference(preference) && !testDeadline) {
+ if (validExecutionPreference(preference)) {
validateGetSupportedOperations(device, message, model);
}
- validatePrepareModel(device, message, model, preference, testDeadline);
+ validatePrepareModel(device, message, model, preference);
}
static uint32_t addOperand(Model* model) {
@@ -744,19 +737,9 @@
}
}
-///////////////////////// DEADLINE /////////////////////////
-
-static void deadlineTest(const sp<IDevice>& device, const Model& model) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Model*) {};
- validate(device, message, model, noop, ExecutionPreference::FAST_SINGLE_ANSWER,
- /*testDeadline=*/true);
-}
-
////////////////////////// ENTRY POINT //////////////////////////////
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported) {
+void validateModel(const sp<IDevice>& device, const Model& model) {
mutateOperandTypeTest(device, model);
mutateOperandRankTest(device, model);
mutateOperandScaleTest(device, model);
@@ -772,9 +755,6 @@
addOperationInputTest(device, model);
addOperationOutputTest(device, model);
mutateExecutionPreferenceTest(device, model);
- if (!prepareModelDeadlineSupported) {
- deadlineTest(device, model);
- }
}
} // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 20f4fe2..5e806e5 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -45,8 +45,7 @@
// that use the request. Note that the request here is passed by value, and any
// mutation to the request does not leave this function.
static void validate(const sp<IPreparedModel>& preparedModel, const std::string& message,
- Request request, const std::function<void(Request*)>& mutation,
- bool testDeadline = false) {
+ Request request, const std::function<void(Request*)>& mutation) {
mutation(&request);
// We'd like to test both with timing requested and without timing
@@ -59,18 +58,13 @@
};
MeasureTiming measure = (hash & 1) ? MeasureTiming::YES : MeasureTiming::NO;
- OptionalTimePoint deadline;
- if (testDeadline) {
- deadline.nanosecondsSinceEpoch(std::numeric_limits<uint64_t>::max());
- }
-
// asynchronous
{
SCOPED_TRACE(message + " [execute_1_3]");
sp<ExecutionCallback> executionCallback = new ExecutionCallback();
Return<ErrorStatus> executeLaunchStatus =
- preparedModel->execute_1_3(request, measure, deadline, {}, executionCallback);
+ preparedModel->execute_1_3(request, measure, {}, {}, executionCallback);
ASSERT_TRUE(executeLaunchStatus.isOk());
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeLaunchStatus));
@@ -88,7 +82,7 @@
SCOPED_TRACE(message + " [executeSynchronously_1_3]");
Return<void> executeStatus = preparedModel->executeSynchronously_1_3(
- request, measure, deadline, {},
+ request, measure, {}, {},
[](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes,
const Timing& timing) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -100,7 +94,7 @@
// burst
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
- if (!testDeadline) {
+ {
SCOPED_TRACE(message + " [burst]");
ASSERT_TRUE(nn::compliantWithV1_0(request));
@@ -143,7 +137,7 @@
{
SCOPED_TRACE(message + " [executeFenced]");
Return<void> ret =
- preparedModel->executeFenced(request, {}, MeasureTiming::NO, deadline, {}, {},
+ preparedModel->executeFenced(request, {}, MeasureTiming::NO, {}, {}, {},
[](ErrorStatus error, const hidl_handle& handle,
const sp<IFencedExecutionCallback>& callback) {
ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
@@ -174,23 +168,11 @@
}
}
-///////////////////////// DEADLINE ////////////////////////////////////
-
-static void deadlineTest(const sp<IPreparedModel>& preparedModel, const Request& request) {
- const std::string message = "deadlineTest: deadline not supported";
- const auto noop = [](Request*) {};
- validate(preparedModel, message, request, noop, /*testDeadline=*/true);
-}
-
///////////////////////////// ENTRY POINT //////////////////////////////////
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported) {
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request) {
removeInputTest(preparedModel, request);
removeOutputTest(preparedModel, request);
- if (!executionDeadlineSupported) {
- deadlineTest(preparedModel, request);
- }
}
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request) {
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 16341da..5b07034 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -123,11 +123,9 @@
INSTANTIATE_DEVICE_TEST(NeuralnetworksHidlTest);
// Forward declaration from ValidateModel.cpp
-void validateModel(const sp<IDevice>& device, const Model& model,
- bool prepareModelDeadlineSupported);
+void validateModel(const sp<IDevice>& device, const Model& model);
// Forward declaration from ValidateRequest.cpp
-void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request,
- bool executionDeadlineSupported);
+void validateRequest(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateRequest.cpp
void validateRequestFailure(const sp<IPreparedModel>& preparedModel, const Request& request);
// Forward declaration from ValidateBurst.cpp
@@ -147,17 +145,15 @@
ASSERT_TRUE(ret_null.isOk());
}
-void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
- validateModel(device, model, prepareModelDeadlineSupported);
+void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request) {
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
createPreparedModel(device, model, &preparedModel);
if (preparedModel == nullptr) return;
- validateRequest(preparedModel, request, executionDeadlineSupported);
+ validateRequest(preparedModel, request);
validateExecuteFenced(preparedModel, request);
// TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
@@ -166,12 +162,10 @@
validateBurst(preparedModel, request10);
}
-void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request,
- std::pair<bool, bool> supportsDeadlines) {
- const bool prepareModelDeadlineSupported = supportsDeadlines.first;
+void validateFailure(const sp<IDevice>& device, const Model& model, const Request& request) {
// TODO: Should this always succeed?
// What if the invalid input is part of the model (i.e., a parameter).
- validateModel(device, model, prepareModelDeadlineSupported);
+ validateModel(device, model);
// Create IPreparedModel.
sp<IPreparedModel> preparedModel;
@@ -185,9 +179,9 @@
const Model model = createModel(kTestModel);
const Request request = nn::convertToV1_3(createRequest(kTestModel));
if (kTestModel.expectFailure) {
- validateFailure(kDevice, model, request, mSupportsDeadlines);
+ validateFailure(kDevice, model, request);
} else {
- validateEverything(kDevice, model, request, mSupportsDeadlines);
+ validateEverything(kDevice, model, request);
}
}
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index 182985e..85c4f99 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -34,7 +34,6 @@
"android.hardware.radio@1.0",
"android.hardware.radio.config@1.0",
"android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.3",
],
header_libs: ["radio.util.header@1.0"],
test_suites: ["general-tests"]
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
index c29ebf9..a5d236d 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_test.cpp
@@ -47,9 +47,9 @@
EXPECT_EQ(serial, radioRsp_v1_5->rspInfo.serial);
EXPECT_EQ(RadioError::NONE, radioRsp_v1_5->rspInfo.error);
- sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig =
+ sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
::testing::VtsHalHidlTargetTestBase::getService<
- ::android::hardware::radio::config::V1_3::IRadioConfig>();
+ ::android::hardware::radio::config::V1_1::IRadioConfig>();
/* Enforce Vts tesing with RadioConfig is existed. */
ASSERT_NE(nullptr, radioConfig.get());
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
index 01cf40a..a7c1cdc 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
+++ b/radio/1.5/vts/functional/radio_hidl_hal_utils_v1_5.h
@@ -22,7 +22,7 @@
#include <condition_variable>
#include <mutex>
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
#include <android/hardware/radio/1.5/IRadio.h>
#include <android/hardware/radio/1.5/IRadioIndication.h>
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
deleted file mode 100644
index 7360270..0000000
--- a/radio/config/1.3/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
- name: "android.hardware.radio.config@1.3",
- root: "android.hardware",
- vndk: {
- enabled: true,
- },
- srcs: [
- "types.hal",
- "IRadioConfig.hal",
- "IRadioConfigIndication.hal",
- "IRadioConfigResponse.hal",
- ],
- interfaces: [
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio@1.0",
- "android.hardware.radio@1.1",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- "android.hidl.base@1.0",
- ],
- gen_java: true,
-}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
deleted file mode 100644
index d01f54b..0000000
--- a/radio/config/1.3/IRadioConfig.hal
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.config@1.3;
-
-import @1.1::IRadioConfig;
-
-/**
- * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
- * radio configuration, and it is not associated with any specific modem or slot.
- * All the functions have minimum one parameter:
- * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
- * duration of a method call. If clients provide colliding serials (including passing the same
- * serial to different methods), multiple responses (one for each method call) must still be served.
- */
-interface IRadioConfig extends @1.1::IRadioConfig {
- /**
- * Request current phone capability.
- *
- * @param serial Serial number of request.
- *
- * Response callback is IRadioResponse.getPhoneCapabilityResponse_1_3() which
- * will return <@1.3::PhoneCapability>.
- */
- oneway getPhoneCapability_1_3(int32_t serial);
-};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
deleted file mode 100644
index e13aa1e..0000000
--- a/radio/config/1.3/IRadioConfigResponse.hal
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.config@1.3;
-
-import android.hardware.radio@1.0::RadioResponseInfo;
-import @1.2::IRadioConfigResponse;
-import @1.3::PhoneCapability;
-
-/**
- * Interface declaring response functions to solicited radio config requests.
- */
-interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
- /**
- * @param info Response info struct containing response type, serial no. and error
- * @param phoneCapability <@1.3::PhoneCapability> it defines modem's capability for example
- * how many logical modems it has, how many data connections it supports.
- *
- * Valid errors returned:
- * RadioError:NONE
- * RadioError:RADIO_NOT_AVAILABLE
- */
- oneway getPhoneCapabilityResponse_1_3(RadioResponseInfo info, PhoneCapability phoneCapability);
-};
diff --git a/radio/config/1.3/default/Android.bp b/radio/config/1.3/default/Android.bp
deleted file mode 100644
index 163c5c5..0000000
--- a/radio/config/1.3/default/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-cc_binary {
- name: "android.hardware.radio.config@1.3-service",
- init_rc: ["android.hardware.radio.config@1.3-service.rc"],
- relative_install_path: "hw",
- vintf_fragments: ["radio-config-default.xml"],
- vendor: true,
- srcs: [
- "RadioConfig.cpp",
- "RadioConfigIndication.cpp",
- "RadioConfigResponse.cpp",
- "service.cpp",
- ],
- shared_libs: [
- "libhidlbase",
- "liblog",
- "libutils",
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio.config@1.3",
- "android.hardware.radio@1.0",
- "android.hardware.radio@1.1",
- "android.hardware.radio@1.2",
- "android.hardware.radio@1.3",
- "android.hardware.radio@1.4",
- "android.hardware.radio@1.5",
- ],
-}
diff --git a/radio/config/1.3/default/RadioConfig.cpp b/radio/config/1.3/default/RadioConfig.cpp
deleted file mode 100644
index 01e98f1..0000000
--- a/radio/config/1.3/default/RadioConfig.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfig.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
-Return<void> RadioConfig::setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication) {
- mRadioConfigResponse = radioConfigResponse;
- mRadioConfigIndication = radioConfigIndication;
-
- mRadioConfigResponseV1_3 =
- V1_3::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_3 =
- V1_3::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_3 == nullptr || mRadioConfigIndicationV1_3 == nullptr) {
- mRadioConfigResponseV1_3 = nullptr;
- mRadioConfigIndicationV1_3 = nullptr;
- }
-
- mRadioConfigResponseV1_2 =
- V1_2::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_2 =
- V1_2::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_2 == nullptr || mRadioConfigIndicationV1_2 == nullptr) {
- mRadioConfigResponseV1_2 = nullptr;
- mRadioConfigIndicationV1_2 = nullptr;
- }
-
- mRadioConfigResponseV1_1 =
- V1_1::IRadioConfigResponse::castFrom(mRadioConfigResponse).withDefault(nullptr);
- mRadioConfigIndicationV1_1 =
- V1_1::IRadioConfigIndication::castFrom(mRadioConfigIndication).withDefault(nullptr);
- if (mRadioConfigResponseV1_1 == nullptr || mRadioConfigIndicationV1_1 == nullptr) {
- mRadioConfigResponseV1_1 = nullptr;
- mRadioConfigIndicationV1_1 = nullptr;
- }
-
- return Void();
-}
-
-Return<void> RadioConfig::getSimSlotsStatus(int32_t /* serial */) {
- hidl_vec<V1_0::SimSlotStatus> slotStatus;
- RadioResponseInfo info;
- mRadioConfigResponse->getSimSlotsStatusResponse(info, slotStatus);
- return Void();
-}
-
-Return<void> RadioConfig::setSimSlotsMapping(int32_t /* serial */,
- const hidl_vec<uint32_t>& /* slotMap */) {
- RadioResponseInfo info;
- mRadioConfigResponse->setSimSlotsMappingResponse(info);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability(int32_t /* serial */) {
- V1_1::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getPhoneCapabilityResponse(info, phoneCapability);
- return Void();
-}
-
-Return<void> RadioConfig::setPreferredDataModem(int32_t /* serial */, uint8_t /* modemId */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setPreferredDataModemResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::setModemsConfig(int32_t /* serial */,
- const V1_1::ModemsConfig& /* modemsConfig */) {
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->setModemsConfigResponse(info);
- return Void();
-}
-
-Return<void> RadioConfig::getModemsConfig(int32_t /* serial */) {
- V1_1::ModemsConfig modemsConfig;
- RadioResponseInfo info;
- mRadioConfigResponseV1_1->getModemsConfigResponse(info, modemsConfig);
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
-Return<void> RadioConfig::getPhoneCapability_1_3(int32_t /* serial */) {
- V1_3::PhoneCapability phoneCapability;
- RadioResponseInfo info;
- mRadioConfigResponseV1_3->getPhoneCapabilityResponse_1_3(info, phoneCapability);
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfig.h b/radio/config/1.3/default/RadioConfig.h
deleted file mode 100644
index 57ff368..0000000
--- a/radio/config/1.3/default/RadioConfig.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_memory;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct RadioConfig : public V1_3::IRadioConfig {
- sp<V1_0::IRadioConfigResponse> mRadioConfigResponse;
- sp<V1_0::IRadioConfigIndication> mRadioConfigIndication;
- sp<V1_1::IRadioConfigResponse> mRadioConfigResponseV1_1;
- sp<V1_1::IRadioConfigIndication> mRadioConfigIndicationV1_1;
- sp<V1_2::IRadioConfigResponse> mRadioConfigResponseV1_2;
- sp<V1_2::IRadioConfigIndication> mRadioConfigIndicationV1_2;
- sp<V1_3::IRadioConfigResponse> mRadioConfigResponseV1_3;
- sp<V1_3::IRadioConfigIndication> mRadioConfigIndicationV1_3;
-
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
- Return<void> setResponseFunctions(
- const sp<V1_0::IRadioConfigResponse>& radioConfigResponse,
- const sp<V1_0::IRadioConfigIndication>& radioConfigIndication);
- Return<void> getSimSlotsStatus(int32_t serial);
- Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap);
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
- Return<void> getPhoneCapability(int32_t serial);
- Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId);
- Return<void> setModemsConfig(int32_t serial, const V1_1::ModemsConfig& modemsConfig);
- Return<void> getModemsConfig(int32_t serial);
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfig follow.
- Return<void> getPhoneCapability_1_3(int32_t serial);
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIG_H
diff --git a/radio/config/1.3/default/RadioConfigIndication.cpp b/radio/config/1.3/default/RadioConfigIndication.cpp
deleted file mode 100644
index 608fa1c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfigIndication.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged(
- RadioIndicationType /* type */, const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
-Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
- RadioIndicationType /* type */, const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigIndication.h b/radio/config/1.3/default/RadioConfigIndication.h
deleted file mode 100644
index c92446c..0000000
--- a/radio/config/1.3/default/RadioConfigIndication.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::V1_0;
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-struct RadioConfigIndication : public IRadioConfigIndication {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged(RadioIndicationType type,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigIndication follow.
- Return<void> simSlotsStatusChanged_1_2(
- RadioIndicationType type, const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGINDICATION_H
diff --git a/radio/config/1.3/default/RadioConfigResponse.cpp b/radio/config/1.3/default/RadioConfigResponse.cpp
deleted file mode 100644
index 1d48a13..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "RadioConfigResponse.h"
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& /* info */, const V1_1::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- // TODO implement
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(
- const RadioResponseInfo& /* info */, const V1_1::ModemsConfig& /* modemsConfig */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- // TODO implement
- return Void();
-}
-
-// Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& /* info */, const V1_3::PhoneCapability& /* phoneCapability */) {
- // TODO implement
- return Void();
-}
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
diff --git a/radio/config/1.3/default/RadioConfigResponse.h b/radio/config/1.3/default/RadioConfigResponse.h
deleted file mode 100644
index dc169bb..0000000
--- a/radio/config/1.3/default/RadioConfigResponse.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-#define ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
-
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <hidl/MQDescriptor.h>
-#include <hidl/Status.h>
-
-namespace android {
-namespace hardware {
-namespace radio {
-namespace config {
-namespace V1_3 {
-namespace implementation {
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-struct RadioConfigResponse : public IRadioConfigResponse {
- // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse(
- const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus) override;
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info) override;
-
- // Methods from ::android::hardware::radio::config::V1_1::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability) override;
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info) override;
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info) override;
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& modemsConfig) override;
-
- // Methods from ::android::hardware::radio::config::V1_2::IRadioConfigResponse follow.
- Return<void> getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus) override;
-
- // Methods from ::android::hardware::radio::config::V1_3::IRadioConfigResponse follow.
- Return<void> getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) override;
-};
-
-} // namespace implementation
-} // namespace V1_3
-} // namespace config
-} // namespace radio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_3_RADIOCONFIGRESPONSE_H
diff --git a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc b/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
deleted file mode 100644
index 6df9b52..0000000
--- a/radio/config/1.3/default/android.hardware.radio.config@1.3-service.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service vendor.radio-config-hal-1-3 /vendor/bin/hw/android.hardware.radio.config@1.3-service
- interface android.hardware.radio.config@1.0::IRadioConfig default
- interface android.hardware.radio.config@1.1::IRadioConfig default
- interface android.hardware.radio.config@1.3::IRadioConfig default
- class hal
- user system
- group system
diff --git a/radio/config/1.3/default/radio-config-default.xml b/radio/config/1.3/default/radio-config-default.xml
deleted file mode 100644
index 72f363e..0000000
--- a/radio/config/1.3/default/radio-config-default.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, The Android Open Source Project.
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest version="1.0" type="device">
- <hal format="hidl">
- <name>android.hardware.radio.config</name>
- <transport>hwbinder</transport>
- <version>1.3</version>
- <interface>
- <name>IRadioConfig</name>
- <instance>default</instance>
- </interface>
- </hal>
-</manifest>
diff --git a/radio/config/1.3/default/service.cpp b/radio/config/1.3/default/service.cpp
deleted file mode 100644
index b1e6736..0000000
--- a/radio/config/1.3/default/service.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.1 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.1
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "android.hardware.radio.config@1.3-service"
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <hidl/HidlTransportSupport.h>
-
-#include "RadioConfig.h"
-
-using android::OK;
-using android::sp;
-using android::status_t;
-using android::hardware::configureRpcThreadpool;
-using android::hardware::joinRpcThreadpool;
-using android::hardware::radio::config::V1_3::IRadioConfig;
-using android::hardware::radio::config::V1_3::implementation::RadioConfig;
-
-int main() {
- configureRpcThreadpool(1, true);
- sp<IRadioConfig> radioConfig = new RadioConfig;
- const status_t status = radioConfig->registerAsService();
- ALOGW_IF(status != OK, "Could not register IRadioConfig 1.3");
- ALOGD("Default service is ready.");
-
- joinRpcThreadpool();
- return 1;
-}
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
deleted file mode 100644
index aef0ff8..0000000
--- a/radio/config/1.3/types.hal
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.radio.config@1.3;
-
-import android.hardware.radio@1.1::GeranBands;
-import android.hardware.radio@1.1::EutranBands;
-import android.hardware.radio@1.4::RadioAccessFamily;
-import android.hardware.radio@1.5::NgranBands;
-import android.hardware.radio@1.5::UtranBands;
-
-/** Type for the SIM slot. */
-enum SlotType : int32_t {
- /** Slot type for UICC/pSIM (physical SIM). */
- UICC = 1,
- /** Slot type for iUICC/iSIM (integrated SIM). */
- IUICC = 2,
- /** Slot type for eUICC/eSIM (embedded SIM). */
- EUICC = 3,
- /** Slot type for soft SIM (no physical SIM). */
- SOFT_SIM = 4,
-};
-
-/** A field in PhoneCapability that holds information about the SIM slot. */
-struct SimSlotCapability {
- /** Corresponds to physicalSlotId in Radio@1.2::CardStatus. */
- uint32_t physicalSlotId;
-
- /** Type of slot. */
- SlotType slotType;
-};
-
-/** Bitmask of features that can be supported by a single modem. */
-enum ModemFeatures : int32_t {
- /** 3GPP2 capability. */
- THREE_GPP2_REG = 1 << 0,
- /** 3GPP capability. */
- THREE_GPP_REG = 1 << 1,
- /** CDMA 2000 with EHRPD capability. */
- CDMA2000_EHRPD = 1 << 2,
- /** GSM/EDGE capability. */
- GERAN = 1 << 3,
- /** UMTS capability. */
- UTRAN = 1 << 4,
- /** LTE capability. */
- EUTRAN = 1 << 5,
- /** 5G capability. */
- NGRAN = 1 << 6,
- /** 5G dual connectivity capability. */
- EN_DC = 1 << 7,
- /** VoLTE capability (IMS registered). */
- PS_VOICE_REG = 1 << 8,
- /** CS voice call capability. */
- CS_VOICE_SESSION = 1 << 9,
- /** Internet connection capability. */
- INTERACTIVE_DATA_SESSION = 1 << 10,
- /** Dedicated bearer capability. */
- DEDICATED_BEARER = 1 << 11,
- /** Network scanning capability. */
- NETWORK_SCAN = 1 << 12,
- /** CDMA capability for SIM associated with modem. */
- CSIM_APP = 1 << 13,
-};
-
-struct ConcurrentModemFeatures {
- /**
- * A vector of concurrently supportable modem features across all modems.
- * Each entry in the vector is a bitfield of ModemFeatures that can be used
- * concurrently with the other ModemFeatures in that list.
- * Each bitfield must be the full set of features for a single modem.
- *
- * On a Dual-SIM device, each entry will be a vector of length 2.
- * The examples below depict the modemFeatures for four Dual-SIM setups:
- * 1. Only one modem can PS attach (IMS registered).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG)
- * }
- * or
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION),
- * (GERAN_REG | UTRAN_REG | CS_VOICE_SESSION)
- * }
- * 2. Both modems can PS attach (dual VoLTE).
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG)
- * }
- * 3. Both modems can maintain an Internet connection, but they share
- * one RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION)
- * }
- * 4. Both modems can maintain an Internet connection, and they have
- * their own RF hardware.
- * {
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * CS_VOICE_SESSION | INTERACTIVE_DATA_SESSION | DEDICATED_BEARER),
- * (GERAN_REG | UTRAN_REG | EUTRAN_REG | PS_VOICE_REG |
- * INTERACTIVE_DATA_SESSION | DEDICATED_BEARER)
- * }
- */
- vec<bitfield<ModemFeatures>> modemFeatures;
-};
-
-/**
- * Overwritten from @1.1::PhoneCapability to add new capabilities and deprecate
- * maxActiveData, maxActiveInternetData, isInternetLingeringSupported, logicalModemList.
- * Replaces RadioConfig@1.1::ModemInfo and should replace Radio@1.4::RadioCapabilities
- * in the next major version upgrade. In the future, this should be extended instead of overwritten.
- */
-struct PhoneCapability {
- /**
- * 3GPP UE category for UTRAN downlink direction.
- * 25.306 Table 5.1a
- */
- uint8_t utranUeCategoryDl;
- /**
- * 3GPP UE category for UTRAN uplink direction.
- * 25.306 Table 5.1g
- */
- uint8_t utranUeCategoryUl;
- /**
- * 3GPP UE category for EUTRAN downlink direction.
- * 25.306 Table 4.1a
- */
- uint8_t eutranUeCategoryDl;
- /**
- * 3GPP UE category for EUTRAN uplink direction.
- * 25.306 Table 4.1a-2
- */
- uint8_t eutranUeCategoryUl;
-
- /**
- * Length of grace period for switching between logical modems, in milliseconds.
- * Used only when the number of logical modems is greater than the number of
- * Internet connections the device can support, otherwise must be 0.
- */
- uint64_t psDataConnectionLingerTimeMillis;
-
- vec<GeranBands> geranBands;
- vec<UtranBands> utranBands;
- vec<EutranBands> eutranBands;
- vec<NgranBands> ngranBands;
-
- /**
- * 32-bit bitmap of supported Radio@1.4::RadioAccessFamily types.
- * Note that RadioAccessFamily is actually the radio access technologies, so it should be
- * renamed in the next major version upgrade.
- */
- bitfield<RadioAccessFamily> supportedRats;
-
- /**
- * List of unique logical modem UUIDs from Radio@1.4::RadioCapabilities.
- * A UUID is typically "com.xxxx.lmX" where X is the logical modem ID.
- * Must be equal to the number of logical modems in the device.
- * Radio@1.2::RadioConst::MAX_UUID_LENGTH is the max length of each UUID.
- */
- vec<string> logicalModemUuids;
-
- /**
- * List of SIM slot capabilities. The order of physical slot IDs must correspond to
- * the order of modems in logicalModemUuids.
- */
- vec<SimSlotCapability> simSlotCapabilities;
-
- /**
- * A vector of all sets of concurrently supportable modem feature sets. The order of modems
- * in modemFeatures must correspond to the order of modems in logicalModemUuids.
- * Each entry in concurrentFeatureSupport is independent of others in the list
- * and represents a set of concurrently supportable features across all modems.
- * Each entry in ConcurrentModemFeatures::modemFeatures is a bitfield of
- * concurrently supported ModemFeatures for one modem.
- */
- vec<ConcurrentModemFeatures> concurrentFeatureSupport;
-};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
deleted file mode 100644
index 6b28faf..0000000
--- a/radio/config/1.3/vts/functional/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_test {
- name: "VtsHalRadioConfigV1_3TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: [
- "radio_config_hidl_hal_api.cpp",
- "radio_config_hidl_hal_test.cpp",
- "radio_config_response.cpp",
- "VtsHalRadioConfigV1_3TargetTest.cpp",
- ],
- static_libs: [
- "RadioVtsTestUtilBase",
- "android.hardware.radio.config@1.0",
- "android.hardware.radio.config@1.1",
- "android.hardware.radio.config@1.2",
- "android.hardware.radio.config@1.3",
- ],
- header_libs: ["radio.util.header@1.0"],
- test_suites: ["general-tests", "vts-core"],
-}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
deleted file mode 100644
index 3bacacf..0000000
--- a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, RadioConfigHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(
- ::android::hardware::radio::config::V1_3::IRadioConfig::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
deleted file mode 100644
index 7f90023..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
-
-/*
- * Test IRadioConfig.getPhoneCapability_1_3()
- */
-TEST_P(RadioConfigHidlTest, getPhoneCapability_1_3) {
- serial = GetRandomSerialNumber();
- Return<void> res = radioConfig->getPhoneCapability_1_3(serial);
- ASSERT_OK(res);
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(RadioResponseType::SOLICITED, radioConfigRsp->rspInfo.type);
- EXPECT_EQ(serial, radioConfigRsp->rspInfo.serial);
- ALOGI("getPhoneCapability_1_3, rspInfo.error = %s\n",
- toString(radioConfigRsp->rspInfo.error).c_str());
-
- ASSERT_TRUE(CheckAnyOfErrors(
- radioConfigRsp->rspInfo.error,
- {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE, RadioError::INTERNAL_ERR}));
-
- if (radioConfigRsp->rspInfo.error == RadioError ::NONE) {
- int numModems = radioConfigRsp->phoneCap_1_3.logicalModemUuids.size();
- EXPECT_GE(numModems, 0);
- // length of simSlotCapabilities should be equal to length of logicalModemUuids.
- EXPECT_EQ(numModems, radioConfigRsp->phoneCap_1_3.simSlotCapabilities.size());
- // length of modemFeatures in each ConcurrentModemFeatures should be
- // equal to length of logicalModemUuids.
- for (V1_3::ConcurrentModemFeatures cmf :
- radioConfigRsp->phoneCap_1_3.concurrentFeatureSupport) {
- EXPECT_EQ(numModems, cmf.modemFeatures.size());
- }
- }
-}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
deleted file mode 100644
index cd48b25..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-void RadioConfigHidlTest::SetUp() {
- radioConfig = V1_3::IRadioConfig::getService(GetParam());
- ASSERT_NE(nullptr, radioConfig.get());
-
- radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
- ASSERT_NE(nullptr, radioConfigRsp.get());
-
- count_ = 0;
-
- radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
-}
-
-/*
- * Notify that the response message is received.
- */
-void RadioConfigHidlTest::notify(int receivedSerial) {
- std::unique_lock<std::mutex> lock(mtx_);
- if (serial == receivedSerial) {
- count_++;
- cv_.notify_one();
- }
-}
-
-/*
- * Wait till the response message is notified or till TIMEOUT_PERIOD.
- */
-std::cv_status RadioConfigHidlTest::wait() {
- std::unique_lock<std::mutex> lock(mtx_);
-
- std::cv_status status = std::cv_status::no_timeout;
- auto now = std::chrono::system_clock::now();
- while (count_ == 0) {
- status = cv_.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
- if (status == std::cv_status::timeout) {
- return status;
- }
- }
- count_--;
- return status;
-}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
deleted file mode 100644
index b21c7c0..0000000
--- a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-#include <android/hardware/radio/config/1.3/IRadioConfig.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigIndication.h>
-#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
-#include <android/hardware/radio/config/1.3/types.h>
-
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-
-#include "vts_test_util.h"
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::sp;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-
-using ::android::hardware::radio::V1_0::RadioIndicationType;
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-using ::android::hardware::radio::V1_0::RadioResponseType;
-
-#define TIMEOUT_PERIOD 75
-
-class RadioConfigHidlTest;
-
-/* Callback class for radio config response */
-class RadioConfigResponse : public V1_3::IRadioConfigResponse {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioResponseInfo rspInfo;
- V1_1::PhoneCapability phoneCap_1_1;
- V1_3::PhoneCapability phoneCap_1_3;
-
- RadioConfigResponse(RadioConfigHidlTest& parent);
- virtual ~RadioConfigResponse() = default;
-
- /* 1.0 Api */
- Return<void> getSimSlotsStatusResponse(const RadioResponseInfo& info,
- const hidl_vec<V1_0::SimSlotStatus>& slotStatus);
-
- Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
-
- /* 1.1 Api */
- Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
- const V1_1::PhoneCapability& phoneCapability);
-
- Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
-
- Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
- const V1_1::ModemsConfig& mConfig);
-
- Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
-
- /* 1.2 Api */
- Return<void> getSimSlotsStatusResponse_1_2(const RadioResponseInfo& info,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
-
- /* 1.3 Api */
- Return<void> getPhoneCapabilityResponse_1_3(const RadioResponseInfo& info,
- const V1_3::PhoneCapability& phoneCapability);
-};
-
-/* Callback class for radio config indication */
-class RadioConfigIndication : public V1_3::IRadioConfigIndication {
- protected:
- RadioConfigHidlTest& parent;
-
- public:
- RadioConfigIndication(RadioConfigHidlTest& parent);
- virtual ~RadioConfigIndication() = default;
-
- /* 1.2 Api */
- Return<void> simSlotsStatusChanged_1_2(RadioIndicationType type,
- const hidl_vec<V1_2::SimSlotStatus>& slotStatus);
-};
-
-// The main test class for Radio config HIDL.
-class RadioConfigHidlTest : public ::testing::TestWithParam<std::string> {
- protected:
- std::mutex mtx_;
- std::condition_variable cv_;
- int count_;
-
- public:
- virtual void SetUp() override;
-
- /* Used as a mechanism to inform the test about data/event callback */
- void notify(int receivedSerial);
-
- /* Test code calls this function to wait for response */
- std::cv_status wait();
-
- void updateSimCardStatus();
-
- /* Serial number for radio request */
- int serial;
-
- /* radio config service handle */
- sp<V1_3::IRadioConfig> radioConfig;
-
- /* radio config response handle */
- sp<RadioConfigResponse> radioConfigRsp;
-};
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
deleted file mode 100644
index 22098d3..0000000
--- a/radio/config/1.3/vts/functional/radio_config_response.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <radio_config_hidl_hal_utils.h>
-
-using namespace ::android::hardware::radio::config;
-
-using ::android::hardware::hidl_vec;
-
-using ::android::hardware::radio::V1_0::RadioResponseInfo;
-
-RadioConfigResponse::RadioConfigResponse(RadioConfigHidlTest& parent) : parent(parent) {}
-
-/* 1.0 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_0::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.1 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
- const RadioResponseInfo& info, const V1_1::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_1 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
-
-Return<void> RadioConfigResponse::setPreferredDataModemResponse(
- const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::getModemsConfigResponse(const RadioResponseInfo& /* info */,
- const V1_1::ModemsConfig& /* mConfig */) {
- return Void();
-}
-
-Return<void> RadioConfigResponse::setModemsConfigResponse(const RadioResponseInfo& /* info */) {
- return Void();
-}
-
-/* 1.2 Apis */
-Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
- const RadioResponseInfo& /* info */,
- const hidl_vec<V1_2::SimSlotStatus>& /* slotStatus */) {
- return Void();
-}
-
-/* 1.3 Apis */
-Return<void> RadioConfigResponse::getPhoneCapabilityResponse_1_3(
- const RadioResponseInfo& info, const V1_3::PhoneCapability& phoneCapability) {
- rspInfo = info;
- phoneCap_1_3 = phoneCapability;
- parent.notify(info.serial);
- return Void();
-}
diff --git a/sensors/2.0/vts/functional/Android.bp b/sensors/2.0/vts/functional/Android.bp
index 22a5091..08c59b6 100644
--- a/sensors/2.0/vts/functional/Android.bp
+++ b/sensors/2.0/vts/functional/Android.bp
@@ -36,7 +36,7 @@
"android.hardware.sensors@2.1",
"libfmq",
"VtsHalSensorsTargetTestUtils",
- "VtsHalSensorsV2_XTargetTest",
+ "VtsHalSensorsV2_0TargetTest-lib",
],
test_suites: [
"general-tests",
diff --git a/sensors/2.1/vts/functional/Android.bp b/sensors/2.1/vts/functional/Android.bp
index c92bab3..c4f5e9d 100644
--- a/sensors/2.1/vts/functional/Android.bp
+++ b/sensors/2.1/vts/functional/Android.bp
@@ -18,7 +18,6 @@
name: "VtsHalSensorsV2_1TargetTest",
cflags: [
"-DLOG_TAG=\"sensors_hidl_hal_test\"",
- "-DSENSORS_HAL_2_1",
],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
@@ -39,7 +38,7 @@
"android.hardware.sensors@2.1",
"libfmq",
"VtsHalSensorsTargetTestUtils",
- "VtsHalSensorsV2_XTargetTest",
+ "VtsHalSensorsV2_1TargetTest-lib",
],
test_suites: [
"general-tests",
diff --git a/sensors/common/vts/2_X/Android.bp b/sensors/common/vts/2_X/Android.bp
index 8cf1486..8cdb5d1 100644
--- a/sensors/common/vts/2_X/Android.bp
+++ b/sensors/common/vts/2_X/Android.bp
@@ -14,8 +14,8 @@
// limitations under the License.
//
-cc_test_library {
- name: "VtsHalSensorsV2_XTargetTest",
+cc_defaults {
+ name: "VtsHalSensorsV2_XTargetTest-defaults",
cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
defaults: ["VtsHalTargetTestDefaults"],
srcs: [
@@ -42,3 +42,14 @@
"VtsHalSensorsTargetTestUtils",
],
}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_0TargetTest-lib",
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
+
+cc_test_library {
+ name: "VtsHalSensorsV2_1TargetTest-lib",
+ cflags: ["-DSENSORS_HAL_2_1"],
+ defaults: ["VtsHalSensorsV2_XTargetTest-defaults"],
+}
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
index 46ac3ee..d857be1 100644
--- a/wifi/1.4/vts/functional/Android.bp
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -22,7 +22,8 @@
"VtsHalWifiV1_4TargetTest.cpp",
"wifi_ap_iface_hidl_test.cpp",
"wifi_chip_hidl_test.cpp",
- "wifi_nan_iface_hidl_test.cpp"
+ "wifi_nan_iface_hidl_test.cpp",
+ "wifi_rtt_controller_hidl_test.cpp",
],
static_libs: [
"VtsHalWifiV1_0TargetTestUtil",
@@ -31,7 +32,10 @@
"android.hardware.wifi@1.2",
"android.hardware.wifi@1.3",
"android.hardware.wifi@1.4",
- "libwifi-system-iface"
+ "libwifi-system-iface",
],
- test_suites: ["general-tests", "vts-core"],
+ test_suites: [
+ "general-tests",
+ "vts-core",
+ ],
}
diff --git a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
index e03c776..1c39550 100644
--- a/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_chip_hidl_test.cpp
@@ -19,6 +19,7 @@
#undef NAN // NAN is defined in bionic/libc/include/math.h:38
+#include <android/hardware/wifi/1.3/IWifiStaIface.h>
#include <android/hardware/wifi/1.4/IWifi.h>
#include <android/hardware/wifi/1.4/IWifiChip.h>
#include <android/hardware/wifi/1.4/IWifiChipEventCallback.h>
@@ -34,10 +35,12 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::ChipModeId;
using ::android::hardware::wifi::V1_0::IfaceType;
using ::android::hardware::wifi::V1_0::WifiDebugRingBufferStatus;
using ::android::hardware::wifi::V1_0::WifiStatus;
using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
using ::android::hardware::wifi::V1_4::IWifiChip;
using ::android::hardware::wifi::V1_4::IWifiChipEventCallback;
@@ -110,6 +113,16 @@
};
protected:
+ // Helper function to configure the Chip in one of the supported modes.
+ // Most of the non-mode-configuration-related methods require chip
+ // to be first configured.
+ ChipModeId configureChipForIfaceType(IfaceType type, bool expectSuccess) {
+ ChipModeId mode_id;
+ EXPECT_EQ(expectSuccess,
+ configureChipToSupportIfaceType(wifi_chip_, type, &mode_id));
+ return mode_id;
+ }
+
sp<IWifiChip> wifi_chip_;
private:
@@ -137,6 +150,28 @@
}
}
+/*
+ * createRttController_1_4
+ * Ensures that an instance of the IWifiRttController proxy object is
+ * successfully created.
+ */
+TEST_P(WifiChipHidlTest, createRttController_1_4) {
+ configureChipForIfaceType(IfaceType::STA, true);
+
+ const auto& status_and_iface = HIDL_INVOKE(wifi_chip_, createStaIface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_iface.first.code);
+ sp<IWifiStaIface> iface = IWifiStaIface::castFrom(status_and_iface.second);
+ EXPECT_NE(nullptr, iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip_, createRttController_1_4, iface);
+ if (status_and_controller.first.code !=
+ WifiStatusCode::ERROR_NOT_SUPPORTED) {
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+ }
+}
+
INSTANTIATE_TEST_SUITE_P(
PerInstance, WifiChipHidlTest,
testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
new file mode 100644
index 0000000..726470c
--- /dev/null
+++ b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+
+#undef NAN // NAN is defined in bionic/libc/include/math.h:38
+
+#include <android/hardware/wifi/1.3/IWifiStaIface.h>
+#include <android/hardware/wifi/1.4/IWifi.h>
+#include <android/hardware/wifi/1.4/IWifiChip.h>
+#include <android/hardware/wifi/1.4/IWifiRttController.h>
+#include <android/hardware/wifi/1.4/IWifiRttControllerEventCallback.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "wifi_hidl_call_util.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::wifi::V1_0::CommandId;
+using ::android::hardware::wifi::V1_0::RttBw;
+using ::android::hardware::wifi::V1_0::RttPeerType;
+using ::android::hardware::wifi::V1_0::RttType;
+using ::android::hardware::wifi::V1_0::WifiChannelInfo;
+using ::android::hardware::wifi::V1_0::WifiChannelWidthInMhz;
+using ::android::hardware::wifi::V1_0::WifiStatus;
+using ::android::hardware::wifi::V1_0::WifiStatusCode;
+using ::android::hardware::wifi::V1_3::IWifiStaIface;
+using ::android::hardware::wifi::V1_4::IWifiChip;
+using ::android::hardware::wifi::V1_4::IWifiRttController;
+using ::android::hardware::wifi::V1_4::IWifiRttControllerEventCallback;
+using ::android::hardware::wifi::V1_4::RttCapabilities;
+using ::android::hardware::wifi::V1_4::RttConfig;
+using ::android::hardware::wifi::V1_4::RttPreamble;
+using ::android::hardware::wifi::V1_4::RttResponder;
+using ::android::hardware::wifi::V1_4::RttResult;
+
+/**
+ * Fixture to use for all RTT controller HIDL interface tests.
+ */
+class WifiRttControllerHidlTest : public ::testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ // Make sure to start with a clean state
+ stopWifi(GetInstanceName());
+
+ wifi_rtt_controller_ = getWifiRttController();
+ ASSERT_NE(nullptr, wifi_rtt_controller_.get());
+ }
+
+ virtual void TearDown() override { stopWifi(GetInstanceName()); }
+
+ // A simple test implementation of WifiChipEventCallback.
+ class WifiRttControllerEventCallback
+ : public ::testing::VtsHalHidlTargetCallbackBase<
+ WifiRttControllerHidlTest>,
+ public IWifiRttControllerEventCallback {
+ public:
+ WifiRttControllerEventCallback(){};
+
+ virtual ~WifiRttControllerEventCallback() = default;
+
+ Return<void> onResults(
+ CommandId cmdId __unused,
+ const hidl_vec<::android::hardware::wifi::V1_0::RttResult>& results
+ __unused) {
+ return Void();
+ };
+
+ Return<void> onResults_1_4(CommandId cmdId __unused,
+ const hidl_vec<RttResult>& results
+ __unused) {
+ return Void();
+ };
+ };
+
+ protected:
+ sp<IWifiRttController> wifi_rtt_controller_;
+
+ private:
+ std::string GetInstanceName() { return GetParam(); }
+
+ sp<IWifiRttController> getWifiRttController() {
+ const std::string& instance_name = GetInstanceName();
+
+ sp<IWifiChip> wifi_chip =
+ IWifiChip::castFrom(getWifiChip(instance_name));
+ EXPECT_NE(nullptr, wifi_chip.get());
+
+ sp<IWifiStaIface> wifi_sta_iface =
+ IWifiStaIface::castFrom(getWifiStaIface(instance_name));
+ EXPECT_NE(nullptr, wifi_sta_iface.get());
+
+ const auto& status_and_controller =
+ HIDL_INVOKE(wifi_chip, createRttController_1_4, wifi_sta_iface);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_controller.first.code);
+ EXPECT_NE(nullptr, status_and_controller.second.get());
+
+ return status_and_controller.second.get();
+ }
+};
+
+/*
+ * registerEventCallback_1_4
+ * This test case tests the registerEventCallback_1_4() API which registers
+ * a call back function with the hal implementation
+ *
+ * Note: it is not feasible to test the invocation of the call back function
+ * since event is triggered internally in the HAL implementation, and can not be
+ * triggered from the test case
+ */
+TEST_P(WifiRttControllerHidlTest, RegisterEventCallback_1_4) {
+ sp<WifiRttControllerEventCallback> wifiRttControllerEventCallback =
+ new WifiRttControllerEventCallback();
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, registerEventCallback_1_4,
+ wifiRttControllerEventCallback);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * rangeRequest_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, RangeRequest_1_4) {
+ std::vector<RttConfig> configs;
+ RttConfig config;
+ int cmdId = 55;
+ // Set the config with test data
+ for (int i = 0; i < 6; i++) {
+ config.addr[i] = i;
+ }
+ config.type = RttType::ONE_SIDED;
+ config.peer = RttPeerType::STA;
+ config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+ config.channel.centerFreq = 5765;
+ config.channel.centerFreq0 = 5775;
+ config.channel.centerFreq1 = 0;
+ config.bw = RttBw::BW_80MHZ;
+ config.preamble = RttPreamble::HE;
+ config.mustRequestLci = false;
+ config.mustRequestLcr = false;
+ config.burstPeriod = 0;
+ config.numBurst = 0;
+ config.numFramesPerBurst = 8;
+ config.numRetriesPerRttFrame = 3;
+ config.numRetriesPerFtmr = 3;
+ config.burstDuration = 9;
+ // Insert config in the vector
+ configs.push_back(config);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, rangeRequest_1_4, cmdId, configs);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+/*
+ * getCapabilities_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetCapabilities_1_4) {
+ std::pair<WifiStatus, RttCapabilities> status_and_caps;
+
+ // Invoke the call
+ status_and_caps = HIDL_INVOKE(wifi_rtt_controller_, getCapabilities_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+}
+
+/*
+ * getResponderInfo_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, GetResponderInfo_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+
+ // Invoke the call
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+}
+
+/*
+ * enableResponder_1_4
+ */
+TEST_P(WifiRttControllerHidlTest, EnableResponder_1_4) {
+ std::pair<WifiStatus, RttResponder> status_and_info;
+ int cmdId = 55;
+ WifiChannelInfo channelInfo;
+ channelInfo.width = WifiChannelWidthInMhz::WIDTH_80;
+ channelInfo.centerFreq = 5690;
+ channelInfo.centerFreq0 = 5690;
+ channelInfo.centerFreq1 = 0;
+
+ // Get the responder first
+ status_and_info = HIDL_INVOKE(wifi_rtt_controller_, getResponderInfo_1_4);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_info.first.code);
+
+ // Invoke the call
+ const auto& status =
+ HIDL_INVOKE(wifi_rtt_controller_, enableResponder_1_4, cmdId,
+ channelInfo, 10, status_and_info.second);
+ EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, WifiRttControllerHidlTest,
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ ::android::hardware::wifi::V1_4::IWifi::descriptor)),
+ android::hardware::PrintInstanceNameToString);