Merge "Camera: add buffer size to Stream"
diff --git a/audio/common/all-versions/test/utility/include/utility/EnvironmentTearDown.h b/audio/common/all-versions/test/utility/include/utility/EnvironmentTearDown.h
index 81d92c2..a96d06e 100644
--- a/audio/common/all-versions/test/utility/include/utility/EnvironmentTearDown.h
+++ b/audio/common/all-versions/test/utility/include/utility/EnvironmentTearDown.h
@@ -20,6 +20,7 @@
#include <functional>
#include <list>
+#include <VtsHalHidlTargetTestEnvBase.h>
#include <gtest/gtest.h>
namespace android {
@@ -33,13 +34,13 @@
* Avoid destroying static objects after main return.
* Post main return destruction leads to incorrect gtest timing measurements as
* well as harder debuging if anything goes wrong during destruction. */
-class Environment : public ::testing::Environment {
+class Environment : public ::testing::VtsHalHidlTargetTestEnvBase {
public:
using TearDownFunc = std::function<void()>;
void registerTearDown(TearDownFunc&& tearDown) { tearDowns.push_back(std::move(tearDown)); }
private:
- void TearDown() override {
+ void HidlTearDown() override {
// Call the tear downs in reverse order of insertion
for (auto& tearDown : tearDowns) {
tearDown();
diff --git a/audio/core/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/core/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
index 6c09da7..bb1d26f 100644
--- a/audio/core/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -88,8 +88,13 @@
using namespace ::android::hardware::audio::common::test::utility;
+class AudioHidlTestEnvironment : public ::Environment {
+ public:
+ virtual void registerTestServices() override { registerTestService<IDevicesFactory>(); }
+};
+
// Instance to register global tearDown
-static Environment* environment;
+static AudioHidlTestEnvironment* environment;
class HidlTest : public ::testing::VtsHalHidlTargetTestBase {
protected:
@@ -109,7 +114,8 @@
if (devicesFactory == nullptr) {
environment->registerTearDown([] { devicesFactory.clear(); });
- devicesFactory = ::testing::VtsHalHidlTargetTestBase::getService<IDevicesFactory>();
+ devicesFactory = ::testing::VtsHalHidlTargetTestBase::getService<IDevicesFactory>(
+ environment->getServiceName<IDevicesFactory>("default"));
}
ASSERT_TRUE(devicesFactory != nullptr);
}
@@ -1265,9 +1271,10 @@
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
- environment = new Environment;
+ environment = new AudioHidlTestEnvironment;
::testing::AddGlobalTestEnvironment(environment);
::testing::InitGoogleTest(&argc, argv);
+ environment->init(&argc, argv);
int status = RUN_ALL_TESTS();
return status;
}
diff --git a/authsecret/1.0/IAuthSecret.hal b/authsecret/1.0/IAuthSecret.hal
index d2cb5da..6b573b3 100644
--- a/authsecret/1.0/IAuthSecret.hal
+++ b/authsecret/1.0/IAuthSecret.hal
@@ -24,25 +24,23 @@
*/
interface IAuthSecret {
/**
- * When the primary user correctly enters their credential, this method is
- * passed a secret derived from that credential to prove that their
- * credential is known.
+ * When the primary user is unlocked, this method is passed a secret to
+ * prove that is has been successfully unlocked. The primary user can either
+ * be unlocked by a person entering their credential or by another party
+ * using an escrow token e.g. a device administrator.
*
* The first time this is called, the secret must be used to provision state
- * that depends on the primary user's credential. The same secret is passed
- * on each call until a factory reset after which there must be a new
- * secret.
+ * that depends on the primary user's secret. The same secret must be passed
+ * on each call until the next factory reset.
*
- * The secret must be at lesat 16 bytes.
+ * Upon factory reset, any dependence on the secret must be removed as that
+ * secret is now lost and must never be derived again. A new secret must be
+ * created for the new primary user which must be used to newly provision
+ * state the first time this method is called after factory reset.
+ *
+ * The secret must be at least 16 bytes.
*
* @param secret blob derived from the primary user's credential.
*/
primaryUserCredential(vec<uint8_t> secret);
-
- /**
- * Called from recovery during factory reset. The secret is now lost and can
- * no longer be derived. Any data linked to the secret must be destroyed and
- * any dependence on the secret must be removed.
- */
- factoryReset();
};
diff --git a/authsecret/1.0/default/AuthSecret.cpp b/authsecret/1.0/default/AuthSecret.cpp
index 46a3ec1..f9271e9 100644
--- a/authsecret/1.0/default/AuthSecret.cpp
+++ b/authsecret/1.0/default/AuthSecret.cpp
@@ -29,16 +29,12 @@
return Void();
}
-Return<void> AuthSecret::factoryReset() {
- // Clear all dependency on the secret.
- //
- // With the example of updating a security module, the stored value must be
- // cleared so that the new primary user enrolled as the approver of updates.
- //
- // This implementation does nothing as there is no dependence on the secret.
-
- return Void();
-}
+// Note: on factory reset, clear all dependency on the secret.
+//
+// With the example of updating a security module, the stored value must be
+// cleared so that the new primary user enrolled as the approver of updates.
+//
+// This implementation does nothing as there is no dependence on the secret.
} // namespace implementation
} // namespace V1_0
diff --git a/authsecret/1.0/default/AuthSecret.h b/authsecret/1.0/default/AuthSecret.h
index edb49b8..387fa67 100644
--- a/authsecret/1.0/default/AuthSecret.h
+++ b/authsecret/1.0/default/AuthSecret.h
@@ -22,7 +22,6 @@
struct AuthSecret : public IAuthSecret {
// Methods from ::android::hardware::authsecret::V1_0::IAuthSecret follow.
Return<void> primaryUserCredential(const hidl_vec<uint8_t>& secret) override;
- Return<void> factoryReset() override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
diff --git a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
index b0cbd91..a610a75 100644
--- a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
+++ b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
@@ -30,68 +30,42 @@
virtual void SetUp() override {
authsecret = ::testing::VtsHalHidlTargetTestBase::getService<IAuthSecret>();
ASSERT_NE(authsecret, nullptr);
- authsecret->factoryReset();
+
+ // All tests must enroll the correct secret first as this cannot be changed
+ // without a factory reset and the order of tests could change.
+ authsecret->primaryUserCredential(CORRECT_SECRET);
}
sp<IAuthSecret> authsecret;
+ hidl_vec<uint8_t> CORRECT_SECRET{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
+ hidl_vec<uint8_t> WRONG_SECRET{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
};
/* Provision the primary user with a secret. */
TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredential) {
- hidl_vec<uint8_t> secret{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
- authsecret->primaryUserCredential(secret);
-}
-
-/* Provision the primary user with a large secret. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialWithLargeSecret) {
- hidl_vec<uint8_t> secret{89, 233, 52, 29, 130, 210, 229, 170, 124, 102, 56, 238, 198,
- 199, 246, 152, 185, 123, 155, 215, 29, 252, 30, 70, 118, 29,
- 149, 36, 222, 203, 163, 7, 72, 56, 247, 19, 198, 76, 71,
- 37, 120, 201, 220, 70, 150, 18, 23, 22, 236, 57, 184, 86,
- 190, 122, 210, 207, 74, 51, 222, 157, 74, 196, 86, 208};
- authsecret->primaryUserCredential(secret);
+ // Secret provisioned by SetUp()
}
/* Provision the primary user with a secret and pass the secret again. */
TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndPassAgain) {
- hidl_vec<uint8_t> secret{64, 2, 3, 0, 5, 6, 7, 172, 9, 10, 11, 255, 13, 14, 15, 83};
- authsecret->primaryUserCredential(secret);
- authsecret->primaryUserCredential(secret);
+ // Secret provisioned by SetUp()
+ authsecret->primaryUserCredential(CORRECT_SECRET);
}
/* Provision the primary user with a secret and pass the secret again repeatedly. */
TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndPassAgainMultipleTimes) {
- hidl_vec<uint8_t> secret{1, 2, 34, 4, 5, 6, 7, 8, 9, 105, 11, 12, 13, 184, 15, 16};
- authsecret->primaryUserCredential(secret);
+ // Secret provisioned by SetUp()
constexpr int N = 5;
for (int i = 0; i < N; ++i) {
- authsecret->primaryUserCredential(secret);
+ authsecret->primaryUserCredential(CORRECT_SECRET);
}
}
-/* Factory reset before provisioning the primary user with a secret. */
-TEST_F(AuthSecretHidlTest, factoryResetWithoutProvisioningPrimaryUserCredential) {
- authsecret->factoryReset();
-}
-
-/* Provision the primary user with a secret then factory reset. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndFactoryReset) {
- hidl_vec<uint8_t> secret{1, 24, 124, 240, 5, 6, 7, 8, 9, 13, 11, 12, 189, 14, 195, 16};
- authsecret->primaryUserCredential(secret);
- authsecret->factoryReset();
-}
-
-/* Provision the primary differently after factory reset. */
-TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialDifferentlyAfterFactoryReset) {
- {
- hidl_vec<uint8_t> secret1{19, 0, 65, 20, 65, 12, 7, 8, 9, 13, 29, 12, 189, 32, 195, 16};
- authsecret->primaryUserCredential(secret1);
- }
-
- authsecret->factoryReset();
-
- {
- hidl_vec<uint8_t> secret2{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
- authsecret->primaryUserCredential(secret2);
- }
+/* Provision the primary user with a secret and then pass the wrong secret. This
+ * should never happen and is an framework bug if it does. As the secret is
+ * wrong, the HAL implementation may not be able to function correctly but it
+ * should fail gracefully. */
+TEST_F(AuthSecretHidlTest, provisionPrimaryUserCredentialAndWrongSecret) {
+ // Secret provisioned by SetUp()
+ authsecret->primaryUserCredential(WRONG_SECRET);
}
diff --git a/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc b/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
index 79edad6..c02db08 100644
--- a/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
+++ b/automotive/audiocontrol/1.0/default/android.hardware.automotive.audiocontrol@1.0-service.rc
@@ -1,4 +1,4 @@
-service vendor.evs-hal-mock /vendor/bin/hw/android.hardware.automotive.audiocontrol@1.0-service
+service vendor.audiocontrol-hal-1.0 /vendor/bin/hw/android.hardware.automotive.audiocontrol@1.0-service
class hal
user audioserver
group system
diff --git a/automotive/vehicle/2.0/Android.bp b/automotive/vehicle/2.0/Android.bp
index ffe012e..a0d20f3 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/2.0/Android.bp
@@ -56,6 +56,7 @@
"VehiclePropertyChangeMode",
"VehiclePropertyGroup",
"VehiclePropertyOperation",
+ "VehiclePropertyStatus",
"VehiclePropertyType",
"VehicleRadioConstants",
"VehicleTurnSignal",
@@ -66,6 +67,7 @@
"VmsMessageWithLayerAndPublisherIdIntegerValuesIndex",
"VmsMessageWithLayerIntegerValuesIndex",
"VmsOfferingMessageIntegerValuesIndex",
+ "VmsPublisherInformationIntegerValuesIndex",
"VmsSubscriptionsStateIntegerValuesIndex",
"Wheel",
],
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
index 8e9089d..6086c01 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/SubscriptionManager.h
@@ -49,7 +49,7 @@
}
void addOrUpdateSubscription(const SubscribeOptions &opts);
- bool isSubscribed(int32_t propId, int32_t areaId, SubscribeFlags flags);
+ bool isSubscribed(int32_t propId, SubscribeFlags flags);
std::vector<int32_t> getSubscribedProperties() const;
private:
@@ -87,8 +87,7 @@
/**
* Constructs SubscriptionManager
*
- * @param onPropertyUnsubscribed - this callback function will be called when there are no
- * more client subscribed to particular property.
+ * @param onPropertyUnsubscribed - called when no more clients are subscribed to the property.
*/
SubscriptionManager(const OnPropertyUnsubscribed& onPropertyUnsubscribed)
: mOnPropertyUnsubscribed(onPropertyUnsubscribed),
@@ -115,9 +114,7 @@
const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
SubscribeFlags flags) const;
- std::list<sp<HalClient>> getSubscribedClients(int32_t propId,
- int32_t area,
- SubscribeFlags flags) const;
+ std::list<sp<HalClient>> getSubscribedClients(int32_t propId, SubscribeFlags flags) const;
/**
* If there are no clients subscribed to given properties than callback function provided
* in the constructor will be called.
@@ -125,10 +122,9 @@
void unsubscribe(ClientId clientId, int32_t propId);
private:
std::list<sp<HalClient>> getSubscribedClientsLocked(int32_t propId,
- int32_t area,
SubscribeFlags flags) const;
- bool updateHalEventSubscriptionLocked(const SubscribeOptions &opts, SubscribeOptions* out);
+ bool updateHalEventSubscriptionLocked(const SubscribeOptions& opts, SubscribeOptions* out);
void addClientToPropMapLocked(int32_t propId, const sp<HalClient>& client);
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
index 8203a1e..fd28483 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleHal.h
@@ -48,17 +48,14 @@
/**
* Subscribe to HAL property events. This method might be called multiple
- * times for the same vehicle property to update subscribed areas or sample
- * rate.
+ * times for the same vehicle property to update sample rate.
*
* @param property to subscribe
- * @param areas a bitwise vehicle areas or 0 for all supported areas
* @param sampleRate sample rate in Hz for properties that support sample
* rate, e.g. for properties with
* VehiclePropertyChangeMode::CONTINUOUS
*/
virtual StatusCode subscribe(int32_t property,
- int32_t areas,
float sampleRate) = 0;
/**
diff --git a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
index 05c649b..359bb6d 100644
--- a/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
+++ b/automotive/vehicle/2.0/default/common/include/vhal_v2_0/VehicleObjectPool.h
@@ -191,9 +191,8 @@
VehiclePropValuePool& operator=(VehiclePropValuePool&) = delete;
private:
bool isDisposable(VehiclePropertyType type, size_t vecSize) const {
- return vecSize > mMaxRecyclableVectorSize ||
- VehiclePropertyType::STRING == type ||
- VehiclePropertyType::COMPLEX == type;
+ return vecSize > mMaxRecyclableVectorSize || VehiclePropertyType::STRING == type ||
+ VehiclePropertyType::MIXED == type;
}
RecyclableType obtainDisposable(VehiclePropertyType valueType,
diff --git a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
index 74f0a5f..a7d5f50 100644
--- a/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/SubscriptionManager.cpp
@@ -34,23 +34,12 @@
bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
const SubscribeOptions &newOpts,
SubscribeOptions *outResult) {
-
- int32_t updatedAreas = oldOpts.vehicleAreas;
- if (updatedAreas != kAllSupportedAreas) {
- updatedAreas = newOpts.vehicleAreas != kAllSupportedAreas
- ? updatedAreas | newOpts.vehicleAreas
- : kAllSupportedAreas;
- }
-
float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);
- bool updated = updatedRate > oldOpts.sampleRate
- || updatedAreas != oldOpts.vehicleAreas
- || updatedFlags != oldOpts.flags;
+ bool updated = (updatedRate > oldOpts.sampleRate) || (updatedFlags != oldOpts.flags);
if (updated) {
*outResult = oldOpts;
- outResult->vehicleAreas = updatedAreas;
outResult->sampleRate = updatedRate;
outResult->flags = updatedFlags;
}
@@ -75,15 +64,13 @@
}
bool HalClient::isSubscribed(int32_t propId,
- int32_t areaId,
SubscribeFlags flags) {
auto it = mSubscriptions.find(propId);
if (it == mSubscriptions.end()) {
return false;
}
const SubscribeOptions& opts = it->second;
- bool res = (opts.flags & flags)
- && (opts.vehicleAreas == 0 || areaId == 0 || opts.vehicleAreas & areaId);
+ bool res = (opts.flags & flags);
return res;
}
@@ -139,8 +126,7 @@
MuxGuard g(mLock);
for (const auto& propValue: propValues) {
VehiclePropValue* v = propValue.get();
- auto clients = getSubscribedClientsLocked(
- v->prop, v->areaId, flags);
+ auto clients = getSubscribedClientsLocked(v->prop, flags);
for (const auto& client : clients) {
clientValuesMap[client].push_back(v);
}
@@ -158,21 +144,21 @@
return clientValues;
}
-std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(
- int32_t propId, int32_t area, SubscribeFlags flags) const {
+std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(int32_t propId,
+ SubscribeFlags flags) const {
MuxGuard g(mLock);
- return getSubscribedClientsLocked(propId, area, flags);
+ return getSubscribedClientsLocked(propId, flags);
}
std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
- int32_t propId, int32_t area, SubscribeFlags flags) const {
+ int32_t propId, SubscribeFlags flags) const {
std::list<sp<HalClient>> subscribedClients;
sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
if (propClients.get() != nullptr) {
for (size_t i = 0; i < propClients->size(); i++) {
const auto& client = propClients->itemAt(i);
- if (client->isSubscribed(propId, area, flags)) {
+ if (client->isSubscribed(propId, flags)) {
subscribedClients.push_back(client);
}
}
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
index ae543bb..1918421 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleHalManager.cpp
@@ -142,15 +142,6 @@
return StatusCode::INVALID_ARG;
}
- int32_t areas = isGlobalProp(prop) ? 0 : ops.vehicleAreas;
- if (areas != 0 && ((areas & config->supportedAreas) != areas)) {
- ALOGE("Failed to subscribe property 0x%x. Requested areas 0x%x are "
- "out of supported range of 0x%x", prop, ops.vehicleAreas,
- config->supportedAreas);
- return StatusCode::INVALID_ARG;
- }
-
- ops.vehicleAreas = areas;
ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
}
@@ -164,7 +155,7 @@
}
for (auto opt : updatedOptions) {
- mHal->subscribe(opt.propId, opt.vehicleAreas, opt.sampleRate);
+ mHal->subscribe(opt.propId, opt.sampleRate);
}
return StatusCode::OK;
@@ -224,8 +215,8 @@
void VehicleHalManager::onHalPropertySetError(StatusCode errorCode,
int32_t property,
int32_t areaId) {
- const auto& clients = mSubscriptionManager.getSubscribedClients(
- property, 0, SubscribeFlags::HAL_EVENT);
+ const auto& clients =
+ mSubscriptionManager.getSubscribedClients(property, SubscribeFlags::HAL_EVENT);
for (auto client : clients) {
client->getCallback()->onPropertySetError(errorCode, property, areaId);
@@ -326,8 +317,7 @@
}
void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) {
- auto clients = mSubscriptionManager.getSubscribedClients(
- value.prop, value.areaId, SubscribeFlags::SET_CALL);
+ auto clients = mSubscriptionManager.getSubscribedClients(value.prop, SubscribeFlags::SET_CALL);
for (auto client : clients) {
client->getCallback()->onPropertySet(value);
}
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
index ac1245a..40dd56e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleObjectPool.cpp
@@ -47,6 +47,7 @@
dest->prop = src.prop;
dest->areaId = src.areaId;
+ dest->status = src.status;
dest->timestamp = src.timestamp;
copyVehicleRawValue(&dest->value, src.value);
@@ -82,7 +83,7 @@
}
VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainComplex() {
- return obtain(VehiclePropertyType::COMPLEX);
+ return obtain(VehiclePropertyType::MIXED);
}
VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainRecylable(
@@ -138,18 +139,14 @@
}
bool VehiclePropValuePool::InternalPool::check(VehiclePropValue::RawValue* v) {
- return check(&v->int32Values,
- (VehiclePropertyType::INT32 == mPropType
- || VehiclePropertyType::INT32_VEC == mPropType
- || VehiclePropertyType::BOOLEAN == mPropType))
- && check(&v->floatValues,
- (VehiclePropertyType::FLOAT == mPropType
- || VehiclePropertyType::FLOAT_VEC == mPropType))
- && check(&v->int64Values,
- VehiclePropertyType::INT64 == mPropType)
- && check(&v->bytes,
- VehiclePropertyType::BYTES == mPropType)
- && v->stringValue.size() == 0;
+ return check(&v->int32Values, (VehiclePropertyType::INT32 == mPropType ||
+ VehiclePropertyType::INT32_VEC == mPropType ||
+ VehiclePropertyType::BOOLEAN == mPropType)) &&
+ check(&v->floatValues, (VehiclePropertyType::FLOAT == mPropType ||
+ VehiclePropertyType::FLOAT_VEC == mPropType)) &&
+ check(&v->int64Values, (VehiclePropertyType::INT64 == mPropType ||
+ VehiclePropertyType::INT64_VEC == mPropType)) &&
+ check(&v->bytes, VehiclePropertyType::BYTES == mPropType) && v->stringValue.size() == 0;
}
VehiclePropValue* VehiclePropValuePool::InternalPool::createObject() {
diff --git a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
index 9146fa1..5b6816e 100644
--- a/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
+++ b/automotive/vehicle/2.0/default/common/src/VehicleUtils.cpp
@@ -42,13 +42,14 @@
val->value.floatValues.resize(vecSize);
break;
case VehiclePropertyType::INT64:
+ case VehiclePropertyType::INT64_VEC:
val->value.int64Values.resize(vecSize);
break;
case VehiclePropertyType::BYTES:
val->value.bytes.resize(vecSize);
break;
case VehiclePropertyType::STRING:
- case VehiclePropertyType::COMPLEX:
+ case VehiclePropertyType::MIXED:
break; // Valid, but nothing to do.
default:
ALOGE("createVehiclePropValue: unknown type: %d", type);
@@ -68,6 +69,7 @@
case VehiclePropertyType::FLOAT_VEC:
return value.floatValues.size();
case VehiclePropertyType::INT64:
+ case VehiclePropertyType::INT64_VEC:
return value.int64Values.size();
case VehiclePropertyType::BYTES:
return value.bytes.size();
@@ -112,6 +114,7 @@
void shallowCopy(VehiclePropValue* dest, const VehiclePropValue& src) {
dest->prop = src.prop;
dest->areaId = src.areaId;
+ dest->status = src.status;
dest->timestamp = src.timestamp;
shallowCopyHidlVec(&dest->value.int32Values, src.value.int32Values);
shallowCopyHidlVec(&dest->value.int64Values, src.value.int64Values);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 71601a0..18e8c40 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -54,10 +54,8 @@
* floatValues[1] - dispersion defines min and max range relative to initial value
* floatValues[2] - increment, with every timer tick the value will be incremented by this amount
*/
-const int32_t kGenerateFakeDataControllingProperty = 0x0666
- | VehiclePropertyGroup::VENDOR
- | VehicleArea::GLOBAL
- | VehiclePropertyType::COMPLEX;
+const int32_t kGenerateFakeDataControllingProperty =
+ 0x0666 | VehiclePropertyGroup::VENDOR | VehicleArea::GLOBAL | VehiclePropertyType::MIXED;
const int32_t kHvacPowerProperties[] = {
toInt(VehicleProperty::HVAC_FAN_SPEED),
@@ -238,7 +236,8 @@
.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1),
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}},
// TODO(bryaneyler): Ideally, this is generated dynamically from
// kHvacPowerProperties.
.configString = "0x12400500,0x12400501" // HVAC_FAN_SPEED,HVAC_FAN_DIRECTION
@@ -249,51 +248,52 @@
.config = {.prop = toInt(VehicleProperty::HVAC_DEFROSTER),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas =
- VehicleAreaWindow::FRONT_WINDSHIELD | VehicleAreaWindow::REAR_WINDSHIELD},
+ .areaConfigs =
+ {VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::FRONT_WINDSHIELD)},
+ VehicleAreaConfig{.areaId = toInt(VehicleAreaWindow::REAR_WINDSHIELD)}}},
.initialValue = {.int32Values = {0}} // Will be used for all areas.
},
{.config = {.prop = toInt(VehicleProperty::HVAC_RECIRC_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
.initialValue = {.int32Values = {1}}},
{.config = {.prop = toInt(VehicleProperty::HVAC_AC_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
.initialValue = {.int32Values = {1}}},
{.config = {.prop = toInt(VehicleProperty::HVAC_AUTO_ON),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1)},
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
.initialValue = {.int32Values = {1}}},
{.config = {.prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1),
- .areaConfigs = {VehicleAreaConfig{.areaId = toInt(VehicleAreaZone::ROW_1),
- .minInt32Value = 1,
- .maxInt32Value = 7}}},
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
+ .minInt32Value = 1,
+ .maxInt32Value = 7}}},
.initialValue = {.int32Values = {3}}},
- {.config =
- {
- .prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
- .access = VehiclePropertyAccess::READ_WRITE,
- .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = toInt(VehicleAreaZone::ROW_1),
- },
+ {.config = {.prop = toInt(VehicleProperty::HVAC_FAN_DIRECTION),
+ .access = VehiclePropertyAccess::READ_WRITE,
+ .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+ .areaConfigs = {VehicleAreaConfig{
+ .areaId = (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT)}}},
.initialValue = {.int32Values = {toInt(VehicleHvacFanDirection::FACE)}}},
{.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT,
.areaConfigs = {VehicleAreaConfig{
.areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
.minFloatValue = 16,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 6bc0522..5118b18 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -138,8 +138,9 @@
return status;
}
} else if (mHvacPowerProps.count(propValue.prop)) {
- auto hvacPowerOn = mPropStore->readValueOrNull(toInt(VehicleProperty::HVAC_POWER_ON),
- toInt(VehicleAreaZone::ROW_1));
+ auto hvacPowerOn = mPropStore->readValueOrNull(
+ toInt(VehicleProperty::HVAC_POWER_ON),
+ (VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT));
if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
&& hvacPowerOn->value.int32Values[0] == 0) {
@@ -177,7 +178,7 @@
void EmulatedVehicleHal::onCreate() {
for (auto& it : kVehicleProperties) {
VehiclePropConfig cfg = it.config;
- int32_t supportedAreas = cfg.supportedAreas;
+ int32_t numAreas = cfg.areaConfigs.size();
if (isDiagnosticProperty(cfg)) {
// do not write an initial empty value for the diagnostic properties
@@ -185,22 +186,26 @@
continue;
}
- // A global property will have supportedAreas = 0
+ // A global property will have only a single area
if (isGlobalProp(cfg.prop)) {
- supportedAreas = 0;
+ numAreas = 1;
}
- // This loop is a do-while so it executes at least once to handle global properties
- do {
- int32_t curArea = supportedAreas;
- supportedAreas &= supportedAreas - 1; // Clear the right-most bit of supportedAreas.
- curArea ^= supportedAreas; // Set curArea to the previously cleared bit.
+ for (int i = 0; i < numAreas; i++) {
+ int32_t curArea;
+
+ if (isGlobalProp(cfg.prop)) {
+ curArea = 0;
+ } else {
+ curArea = cfg.areaConfigs[i].areaId;
+ }
// Create a separate instance for each individual zone
VehiclePropValue prop = {
.prop = cfg.prop,
.areaId = curArea,
};
+
if (it.initialAreaValues.size() > 0) {
auto valueForAreaIt = it.initialAreaValues.find(curArea);
if (valueForAreaIt != it.initialAreaValues.end()) {
@@ -213,8 +218,7 @@
prop.value = it.initialValue;
}
mPropStore->writeValue(prop);
-
- } while (supportedAreas != 0);
+ }
}
initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
@@ -246,8 +250,7 @@
}
}
-StatusCode EmulatedVehicleHal::subscribe(int32_t property, int32_t,
- float sampleRate) {
+StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
if (isContinuousProperty(property)) {
@@ -360,6 +363,7 @@
updatedPropValue->prop = propId;
updatedPropValue->areaId = 0; // Add area support if necessary.
updatedPropValue->timestamp = elapsedRealtimeNano();
+ updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
mPropStore->writeValue(*updatedPropValue);
auto changeMode = mPropStore->getConfigOrDie(propId)->changeMode;
if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) {
@@ -389,7 +393,7 @@
}
void EmulatedVehicleHal::initObd2LiveFrame(const VehiclePropConfig& propConfig) {
- auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::COMPLEX, 0);
+ auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
static_cast<size_t>(propConfig.configArray[1]));
sensorStore->fillPropValue("", liveObd2Frame.get());
@@ -406,7 +410,7 @@
"P0102"
"P0123"};
for (auto&& dtc : sampleDtcs) {
- auto freezeFrame = createVehiclePropValue(VehiclePropertyType::COMPLEX, 0);
+ auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
sensorStore->fillPropValue(dtc, freezeFrame.get());
freezeFrame->prop = OBD2_FREEZE_FRAME;
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index 99d7edb..62fc126 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -53,7 +53,7 @@
VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
StatusCode* outStatus) override;
StatusCode set(const VehiclePropValue& propValue) override;
- StatusCode subscribe(int32_t property, int32_t areas, float sampleRate) override;
+ StatusCode subscribe(int32_t property, float sampleRate) override;
StatusCode unsubscribe(int32_t property) override;
// Methods from EmulatedVehicleHalIface
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index 38cb743..bf7be09 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -138,6 +138,7 @@
VehiclePropValue val = {
.prop = protoVal.prop(),
.areaId = protoVal.area_id(),
+ .status = (VehiclePropertyStatus)protoVal.status(),
.timestamp = elapsedRealtimeNano(),
};
@@ -234,10 +235,6 @@
protoCfg->set_change_mode(toInt(cfg.changeMode));
protoCfg->set_value_type(toInt(getPropType(cfg.prop)));
- if (!isGlobalProp(cfg.prop)) {
- protoCfg->set_supported_areas(cfg.supportedAreas);
- }
-
for (auto& configElement : cfg.configArray) {
protoCfg->add_config_array(configElement);
}
@@ -251,9 +248,10 @@
case VehiclePropertyType::STRING:
case VehiclePropertyType::BOOLEAN:
case VehiclePropertyType::INT32_VEC:
+ case VehiclePropertyType::INT64_VEC:
case VehiclePropertyType::FLOAT_VEC:
case VehiclePropertyType::BYTES:
- case VehiclePropertyType::COMPLEX:
+ case VehiclePropertyType::MIXED:
// Do nothing. These types don't have min/max values
break;
case VehiclePropertyType::INT64:
@@ -291,6 +289,7 @@
protoVal->set_prop(val->prop);
protoVal->set_value_type(toInt(getPropType(val->prop)));
protoVal->set_timestamp(val->timestamp);
+ protoVal->set_status((emulator::VehiclePropStatus)(val->status));
protoVal->set_area_id(val->areaId);
// Copy value data if it is set.
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
index 86433f5..2ef64fb 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/VehicleHalProto.proto
@@ -46,6 +46,12 @@
ERROR_INVALID_OPERATION = 8;
}
+enum VehiclePropStatus {
+ AVAILABLE = 0;
+ UNAVAILABLE = 1;
+ ERROR = 2;
+}
+
message VehicleAreaConfig {
required int32 area_id = 1;
optional sint32 min_int32_value = 2;
@@ -61,7 +67,7 @@
optional int32 access = 2;
optional int32 change_mode = 3;
optional int32 value_type = 4;
- optional int32 supported_areas = 5;
+ optional int32 supported_areas = 5; // Deprecated - DO NOT USE
repeated VehicleAreaConfig area_configs = 6;
optional int32 config_flags = 7;
repeated int32 config_array = 8;
@@ -75,6 +81,7 @@
required int32 prop = 1;
optional int32 value_type = 2;
optional int64 timestamp = 3; // required for valid data from HAL, skipped for set
+ optional VehiclePropStatus status = 10; // required for valid data from HAL, skipped for set
// values
optional int32 area_id = 4;
diff --git a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
index 5688dd6..4865e9e 100644
--- a/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/SubscriptionManager_test.cpp
@@ -51,11 +51,7 @@
}
hidl_vec<SubscribeOptions> subscrToProp1 = {
- SubscribeOptions {
- .propId = PROP1,
- .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
- .flags = SubscribeFlags::HAL_EVENT
- },
+ SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::HAL_EVENT},
};
hidl_vec<SubscribeOptions> subscrToProp2 = {
@@ -66,15 +62,8 @@
};
hidl_vec<SubscribeOptions> subscrToProp1and2 = {
- SubscribeOptions {
- .propId = PROP1,
- .vehicleAreas = toInt(VehicleAreaZone::ROW_1_LEFT),
- .flags = SubscribeFlags::HAL_EVENT
- },
- SubscribeOptions {
- .propId = PROP2,
- .flags = SubscribeFlags::HAL_EVENT
- },
+ SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::HAL_EVENT},
+ SubscribeOptions{.propId = PROP2, .flags = SubscribeFlags::HAL_EVENT},
};
static std::list<sp<IVehicleCallback>> extractCallbacks(
@@ -87,14 +76,11 @@
}
std::list<sp<HalClient>> clientsToProp1() {
- return manager.getSubscribedClients(PROP1,
- toInt(VehicleAreaZone::ROW_1_LEFT),
- SubscribeFlags::DEFAULT);
+ return manager.getSubscribedClients(PROP1, SubscribeFlags::DEFAULT);
}
std::list<sp<HalClient>> clientsToProp2() {
- return manager.getSubscribedClients(PROP2, 0,
- SubscribeFlags::DEFAULT);
+ return manager.getSubscribedClients(PROP2, SubscribeFlags::DEFAULT);
}
void onPropertyUnsubscribed(int propertyId) {
@@ -126,7 +112,6 @@
auto clients = manager.getSubscribedClients(
PROP1,
- toInt(VehicleAreaZone::ROW_1_LEFT),
SubscribeFlags::HAL_EVENT);
ASSERT_ALL_EXISTS({cb1, cb2}, extractCallbacks(clients));
@@ -137,24 +122,14 @@
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(1, cb1, subscrToProp1, &updatedOptions));
- // Wrong zone
- auto clients = manager.getSubscribedClients(
- PROP1,
- toInt(VehicleAreaZone::ROW_2_LEFT),
- SubscribeFlags::HAL_EVENT);
- ASSERT_TRUE(clients.empty());
-
// Wrong prop
- clients = manager.getSubscribedClients(
- toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
- toInt(VehicleAreaZone::ROW_1_LEFT),
- SubscribeFlags::HAL_EVENT);
+ auto clients = manager.getSubscribedClients(toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
+ SubscribeFlags::HAL_EVENT);
ASSERT_TRUE(clients.empty());
// Wrong flag
clients = manager.getSubscribedClients(
PROP1,
- toInt(VehicleAreaZone::ROW_1_LEFT),
SubscribeFlags::SET_CALL);
ASSERT_TRUE(clients.empty());
}
@@ -166,7 +141,6 @@
auto clients = manager.getSubscribedClients(
PROP1,
- toInt(VehicleAreaZone::ROW_1_LEFT),
SubscribeFlags::DEFAULT);
ASSERT_EQ((size_t) 1, clients.size());
ASSERT_EQ(cb1, clients.front()->getCallback());
@@ -176,18 +150,15 @@
ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(1, cb1, {
SubscribeOptions {
.propId = PROP1,
- .vehicleAreas = toInt(VehicleAreaZone::ROW_2),
.flags = SubscribeFlags::DEFAULT
}
}, &updatedOptions));
clients = manager.getSubscribedClients(PROP1,
- toInt(VehicleAreaZone::ROW_1_LEFT),
SubscribeFlags::DEFAULT);
ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
clients = manager.getSubscribedClients(PROP1,
- toInt(VehicleAreaZone::ROW_2),
SubscribeFlags::DEFAULT);
ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
}
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
index 4864d5d..5b195db 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalManager_test.cpp
@@ -106,7 +106,6 @@
}
StatusCode subscribe(int32_t /* property */,
- int32_t /* areas */,
float /* sampleRate */) override {
return StatusCode::OK;
}
@@ -286,6 +285,7 @@
cb->reset();
VehiclePropValue actualValue(*subscribedValue.get());
+ actualValue.status = VehiclePropertyStatus::AVAILABLE;
hal->sendPropEvent(std::move(subscribedValue));
ASSERT_TRUE(cb->waitForExpectedEvents(1)) << "Events received: "
diff --git a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
index 2a06417..3cabcf2 100644
--- a/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
+++ b/automotive/vehicle/2.0/default/tests/VehicleHalTestUtils.h
@@ -29,10 +29,8 @@
namespace vehicle {
namespace V2_0 {
-constexpr int32_t kCustomComplexProperty = 0xbeef
- | VehiclePropertyGroup::VENDOR
- | VehiclePropertyType::COMPLEX
- | VehicleArea::GLOBAL;
+constexpr int32_t kCustomComplexProperty =
+ 0xbeef | VehiclePropertyGroup::VENDOR | VehiclePropertyType::MIXED | VehicleArea::GLOBAL;
const VehiclePropConfig kVehicleProperties[] = {
{
@@ -46,8 +44,6 @@
.prop = toInt(VehicleProperty::HVAC_FAN_SPEED),
.access = VehiclePropertyAccess::READ_WRITE,
.changeMode = VehiclePropertyChangeMode::ON_CHANGE,
- .supportedAreas = static_cast<int32_t>(
- VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
.areaConfigs = {
VehicleAreaConfig {
.areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
@@ -66,8 +62,6 @@
.prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
.access = VehiclePropertyAccess::WRITE,
.changeMode = VehiclePropertyChangeMode::ON_SET,
- .supportedAreas = static_cast<int32_t>(
- VehicleAreaZone::ROW_1_LEFT | VehicleAreaZone::ROW_1_RIGHT),
.areaConfigs = {
VehicleAreaConfig {
.areaId = toInt(VehicleAreaZone::ROW_1_LEFT),
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index 7e42781..f673d1a 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -27,6 +27,7 @@
INT32 = 0x00400000,
INT32_VEC = 0x00410000,
INT64 = 0x00500000,
+ INT64_VEC = 0x00510000,
FLOAT = 0x00600000,
FLOAT_VEC = 0x00610000,
BYTES = 0x00700000,
@@ -35,7 +36,7 @@
* Any combination of scalar or vector types. The exact format must be
* provided in the description of the property.
*/
- COMPLEX = 0x00e00000,
+ MIXED = 0x00e00000,
MASK = 0x00ff0000
};
@@ -315,7 +316,7 @@
WHEEL_TICK = (
0x0306
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
@@ -1673,7 +1674,7 @@
/**
* Vehicle Maps Service (VMS) message
*
- * This property uses COMPLEX data to communicate vms messages.
+ * This property uses MIXED data to communicate vms messages.
*
* Its contents are to be interpreted as follows:
* the indices defined in VmsMessageIntegerValuesIndex are to be used to
@@ -1689,7 +1690,7 @@
VEHICLE_MAP_SERVICE = (
0x0C00
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
/**
@@ -1736,7 +1737,7 @@
OBD2_LIVE_FRAME = (
0x0D00
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
/**
@@ -1766,7 +1767,7 @@
OBD2_FREEZE_FRAME = (
0x0D01
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
/**
@@ -1787,7 +1788,7 @@
OBD2_FREEZE_FRAME_INFO = (
0x0D02
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
/**
@@ -1813,7 +1814,7 @@
OBD2_FREEZE_FRAME_CLEAR = (
0x0D03
| VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
+ | VehiclePropertyType:MIXED
| VehicleArea:GLOBAL),
};
@@ -2169,6 +2170,21 @@
};
/**
+ * Property status is a dynamic value that may change based on the vehicle state.
+ */
+enum VehiclePropertyStatus : int32_t {
+ /** Property is available and behaving normally */
+ AVAILABLE = 0x00,
+ /**
+ * Property is not available, for read and/or write. This is a transient state, as the
+ * property is expected to be available at a later time.
+ */
+ UNAVAILABLE = 0x01,
+ /** There is an error with this property. */
+ ERROR = 0x02,
+};
+
+/**
* Car states.
*
* The driving states determine what features of the UI will be accessible.
@@ -2212,20 +2228,15 @@
ROW_1_LEFT = 0x00000001,
ROW_1_CENTER = 0x00000002,
ROW_1_RIGHT = 0x00000004,
- ROW_1 = 0x00000008,
ROW_2_LEFT = 0x00000010,
ROW_2_CENTER = 0x00000020,
ROW_2_RIGHT = 0x00000040,
- ROW_2 = 0x00000080,
ROW_3_LEFT = 0x00000100,
ROW_3_CENTER = 0x00000200,
ROW_3_RIGHT = 0x00000400,
- ROW_3 = 0x00000800,
ROW_4_LEFT = 0x00001000,
ROW_4_CENTER = 0x00002000,
ROW_4_RIGHT = 0x00004000,
- ROW_4 = 0x00008000,
- WHOLE_CABIN = 0x80000000,
};
/**
@@ -2313,13 +2324,6 @@
VehiclePropertyChangeMode changeMode;
/**
- * Some of the properties may have associated areas (for example, some hvac
- * properties are associated with VehicleAreaZone), in these
- * cases the config may contain an ORed value for the associated areas.
- */
- int32_t supportedAreas;
-
- /**
* Contains per-area configuration.
*/
vec<VehicleAreaConfig> areaConfigs;
@@ -2372,6 +2376,9 @@
*/
int32_t areaId;
+ /** Status of the property */
+ VehiclePropertyStatus status;
+
/**
* Contains value for a single property. Depending on property data type of
* this property (VehiclePropetyType) one field of this structure must be filled in.
@@ -2484,12 +2491,6 @@
int32_t propId;
/**
- * Area ids - this must be a bit mask of areas to subscribe or 0 to subscribe
- * to all areas.
- */
- int32_t vehicleAreas;
-
- /**
* Sample rate in Hz.
*
* Must be provided for properties with
@@ -2894,9 +2895,42 @@
* A message from the VMS service to the subscribers or from the publishers to the VMS service
* with a serialized VMS data packet as defined in the VMS protocol.
*
- * This message type uses enum VmsBaseMessageIntegerValuesIndex.
+ * This message type uses enum VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.
*/
DATA = 12,
+
+ /**
+ * A request from the publishers to the VMS service to get a Publisher ID for a serialized VMS
+ * provider description packet as defined in the VMS protocol.
+ *
+ * This message type uses enum VmsBaseMessageIntegerValuesIndex.
+ */
+ PUBLISHER_ID_REQUEST = 13,
+
+ /**
+ * A response from the VMS service to the publisher that contains a provider description packet
+ * and the publisher ID assigned to it.
+ *
+ * This message type uses enum VmsPublisherInformationIntegerValuesIndex.
+ */
+ PUBLISHER_ID_RESPONSE = 14,
+
+ /**
+ * A request from the subscribers to the VMS service to get information for a Publisher ID.
+ *
+ * This message type uses enum VmsPublisherInformationIntegerValuesIndex.
+ */
+ PUBLISHER_INFORMATION_REQUEST = 15,
+
+ /**
+ * A response from the VMS service to the subscribers that contains a provider description packet
+ * and the publisher ID assigned to it.
+ *
+ * This message type uses enum VmsPublisherInformationIntegerValuesIndex.
+ */
+ PUBLISHER_INFORMATION_RESPONSE = 16,
+
+ LAST_VMS_MESSAGE_TYPE = PUBLISHER_INFORMATION_RESPONSE,
};
/**
@@ -2924,7 +2958,8 @@
/*
* A VMS message with a layer and publisher ID is sent as part of a
- * VmsMessageType.SUBSCRIBE_TO_PUBLISHER and VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER messages.
+ * VmsMessageType.SUBSCRIBE_TO_PUBLISHER, VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER messages and
+ * VmsMessageType.DATA .
*/
enum VmsMessageWithLayerAndPublisherIdIntegerValuesIndex : VmsMessageWithLayerIntegerValuesIndex {
PUBLISHER_ID = 4,
@@ -2993,3 +3028,11 @@
LAYERS_START = 3,
};
+/*
+ * Publishers send the VMS service their information and assigned in response a publisher ID.
+ * Subscribers can request the publisher information for a publisher ID they received in other messages.
+ */
+enum VmsPublisherInformationIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex {
+ PUBLISHER_ID = 1,
+};
+
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp
index 6209cb8..21f81f5 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/1.0/default/Android.bp
@@ -7,7 +7,9 @@
"CameraMetadata.cpp",
"CameraParameters.cpp",
"VendorTagDescriptor.cpp",
- "HandleImporter.cpp"],
+ "HandleImporter.cpp",
+ "Exif.cpp"
+ ],
cflags: [
"-Werror",
"-Wextra",
@@ -17,7 +19,9 @@
"liblog",
"libhardware",
"libcamera_metadata",
- "android.hardware.graphics.mapper@2.0"],
+ "android.hardware.graphics.mapper@2.0",
+ "libexif",
+ ],
include_dirs: ["system/media/private/camera/include"],
export_include_dirs : ["include"]
}
diff --git a/camera/common/1.0/default/Exif.cpp b/camera/common/1.0/default/Exif.cpp
new file mode 100644
index 0000000..3e894f9
--- /dev/null
+++ b/camera/common/1.0/default/Exif.cpp
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CamComm1.0-Exif"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "Exif.h"
+
+extern "C" {
+#include <libexif/exif-data.h>
+}
+
+namespace std {
+
+template <>
+struct default_delete<ExifEntry> {
+ inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
+};
+
+} // namespace std
+
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+
+class ExifUtilsImpl : public ExifUtils {
+ public:
+ ExifUtilsImpl();
+
+ virtual ~ExifUtilsImpl();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize();
+
+ // set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight);
+
+ // sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator);
+
+ // sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space);
+
+ // sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration);
+
+ // sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression);
+
+ // sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast);
+
+ // sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t);
+
+ // sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description);
+
+ // sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator);
+
+ // sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator);
+
+ // sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode);
+
+ // sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program);
+
+ // sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator);
+
+ // sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash);
+
+ // sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator);
+
+ // sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator);
+
+ // sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control);
+
+ // sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude);
+
+ // sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude);
+
+ // sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude);
+
+ // sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method);
+
+ // sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t);
+
+ // sets the length (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length);
+
+ // sets the width (number of columes) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width);
+
+ // sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings);
+
+ // sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source);
+
+ // sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode);
+
+ // sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation);
+
+ // sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit);
+
+ // sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation);
+
+ // sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type);
+
+ // sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness);
+
+ // sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator);
+
+ // sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator);
+
+ // sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time);
+
+ // sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance);
+
+ // sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning);
+
+ // sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make);
+
+ // sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model);
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1(const void* thumbnail_buffer, uint32_t size);
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer();
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length();
+
+ protected:
+ // sets the version of this standard supported.
+ // Returns false if memory allocation fails.
+ virtual bool setExifVersion(const std::string& exif_version);
+
+
+ // Resets the pointers and memories.
+ virtual void reset();
+
+ // Adds a variable length tag to |exif_data_|. It will remove the original one
+ // if the tag exists.
+ // Returns the entry of the tag. The reference count of returned ExifEntry is
+ // two.
+ virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ uint64_t components,
+ unsigned int size);
+
+ // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if
+ // the tag exists.
+ // Returns the entry of the tag. It adds one reference count to returned
+ // ExifEntry.
+ virtual std::unique_ptr<ExifEntry> addEntry(ExifIfd ifd, ExifTag tag);
+
+ // Helpe functions to add exif data with different types.
+ virtual bool setShort(ExifIfd ifd,
+ ExifTag tag,
+ uint16_t value,
+ const std::string& msg);
+
+ virtual bool setLong(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t value,
+ const std::string& msg);
+
+ virtual bool setRational(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t numerator,
+ uint32_t denominator,
+ const std::string& msg);
+
+ virtual bool setSRational(ExifIfd ifd,
+ ExifTag tag,
+ int32_t numerator,
+ int32_t denominator,
+ const std::string& msg);
+
+ virtual bool setString(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ const std::string& buffer,
+ const std::string& msg);
+
+ // Destroys the buffer of APP1 segment if exists.
+ virtual void destroyApp1();
+
+ // The Exif data (APP1). Owned by this class.
+ ExifData* exif_data_;
+ // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but
+ // owned by this class.
+ uint8_t* app1_buffer_;
+ // The length of |app1_buffer_|.
+ unsigned int app1_length_;
+
+};
+
+#define SET_SHORT(ifd, tag, value) \
+ do { \
+ if (setShort(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_LONG(ifd, tag, value) \
+ do { \
+ if (setLong(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_RATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_SRATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setSRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_STRING(ifd, tag, format, buffer) \
+ do { \
+ if (setString(ifd, tag, format, buffer, #tag) == false) \
+ return false; \
+ } while (0);
+
+// This comes from the Exif Version 2.2 standard table 6.
+const char gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0};
+
+static void setLatitudeOrLongitudeData(unsigned char* data, double num) {
+ // Take the integer part of |num|.
+ ExifLong degrees = static_cast<ExifLong>(num);
+ ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
+ ExifLong microseconds =
+ static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
+ exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
+ exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {minutes, 1});
+ exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {microseconds, 1000000});
+}
+
+ExifUtils *ExifUtils::create() {
+ return new ExifUtilsImpl();
+}
+
+ExifUtils::~ExifUtils() {
+}
+
+ExifUtilsImpl::ExifUtilsImpl()
+ : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}
+
+ExifUtilsImpl::~ExifUtilsImpl() {
+ reset();
+}
+
+
+bool ExifUtilsImpl::initialize() {
+ reset();
+ exif_data_ = exif_data_new();
+ if (exif_data_ == nullptr) {
+ ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__);
+ return false;
+ }
+ // set the image options.
+ exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+ exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);
+
+ // set exif version to 2.2.
+ if (!setExifVersion("0220")) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setColorSpace(uint16_t color_space) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE, color_space);
+ return true;
+}
+
+bool ExifUtilsImpl::setComponentsConfiguration(
+ const std::string& components_configuration) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION,
+ EXIF_FORMAT_UNDEFINED, components_configuration);
+ return true;
+}
+
+bool ExifUtilsImpl::setCompression(uint16_t compression) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
+ return true;
+}
+
+bool ExifUtilsImpl::setContrast(uint16_t contrast) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast);
+ return true;
+}
+
+bool ExifUtilsImpl::setDateTime(const struct tm& t) {
+ // The length is 20 bytes including NULL for termination in Exif standard.
+ char str[20];
+ int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
+ t.tm_min, t.tm_sec);
+ if (result != sizeof(str) - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+ std::string buffer(str);
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
+ buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
+ buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setDescription(const std::string& description) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII,
+ description);
+ return true;
+}
+
+bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFlash(uint16_t flash) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash);
+ return true;
+}
+
+bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setGainControl(uint16_t gain_control) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsAltitude(double altitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSAltitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (altitude >= 0) {
+ *refEntry->data = 0;
+ } else {
+ *refEntry->data = 1;
+ altitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSAltitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(altitude * 1000), 1000});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLatitude(double latitude) {
+ const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLatitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (latitude >= 0) {
+ memcpy(refEntry->data, "N", sizeof("N"));
+ } else {
+ memcpy(refEntry->data, "S", sizeof("S"));
+ latitude *= -1;
+ }
+
+ const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLatitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, latitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLongitude(double longitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLongitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (longitude >= 0) {
+ memcpy(refEntry->data, "E", sizeof("E"));
+ } else {
+ memcpy(refEntry->data, "W", sizeof("W"));
+ longitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLongitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, longitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) {
+ std::string buffer =
+ std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
+ SET_STRING(EXIF_IFD_GPS, static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD),
+ EXIF_FORMAT_UNDEFINED, buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) {
+ const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
+ const size_t kGpsDateStampSize = 11;
+ std::unique_ptr<ExifEntry> entry =
+ addVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII,
+ kGpsDateStampSize, kGpsDateStampSize);
+ if (!entry) {
+ ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ int result =
+ snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
+ "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
+ if (result != kGpsDateStampSize - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+
+ const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
+ entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
+ 3 * sizeof(ExifRational));
+ if (!entry) {
+ ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_hour), 1});
+ exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_min), 1});
+ exif_set_rational(entry->data + 2 * sizeof(ExifRational),
+ EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_sec), 1});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setImageHeight(uint32_t length) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, length);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, length);
+ return true;
+}
+
+bool ExifUtilsImpl::setImageWidth(uint32_t width) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, width);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width);
+ return true;
+}
+
+bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso_speed_ratings);
+ return true;
+}
+
+bool ExifUtilsImpl::setLightSource(uint16_t light_source) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source);
+ return true;
+}
+
+bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setOrientation(uint16_t orientation) {
+ /*
+ * Orientation value:
+ * 1 2 3 4 5 6 7 8
+ *
+ * 888888 888888 88 88 8888888888 88 88 8888888888
+ * 88 88 88 88 88 88 88 88 88 88 88 88
+ * 8888 8888 8888 8888 88 8888888888 8888888888 88
+ * 88 88 88 88
+ * 88 88 888888 888888
+ */
+ int value = 1;
+ switch (orientation) {
+ case 90:
+ value = 6;
+ break;
+ case 180:
+ value = 3;
+ break;
+ case 270:
+ value = 8;
+ break;
+ default:
+ break;
+ }
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit);
+ return true;
+}
+
+bool ExifUtilsImpl::setSaturation(uint16_t saturation) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation);
+ return true;
+}
+
+bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type);
+ return true;
+}
+
+bool ExifUtilsImpl::setSharpness(uint16_t sharpness) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness);
+ return true;
+}
+
+bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator,
+ denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII,
+ subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII,
+ subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII,
+ subsec_time);
+ return true;
+}
+
+bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance);
+ return true;
+}
+
+bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning);
+ return true;
+}
+
+bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::generateApp1(const void* thumbnail_buffer, uint32_t size) {
+ destroyApp1();
+ exif_data_->data = const_cast<uint8_t*>(static_cast<const uint8_t*>(thumbnail_buffer));
+ exif_data_->size = size;
+ // Save the result into |app1_buffer_|.
+ exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
+ if (!app1_length_) {
+ ALOGE("%s: Allocate memory for app1_buffer_ failed", __FUNCTION__);
+ return false;
+ }
+ /*
+ * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
+ * be smaller than 65533 because there are two bytes for segment size field.
+ */
+ if (app1_length_ > 65533) {
+ destroyApp1();
+ ALOGE("%s: The size of APP1 segment is too large", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+const uint8_t* ExifUtilsImpl::getApp1Buffer() {
+ return app1_buffer_;
+}
+
+unsigned int ExifUtilsImpl::getApp1Length() {
+ return app1_length_;
+}
+
+bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, exif_version);
+ return true;
+}
+
+bool ExifUtilsImpl::setMake(const std::string& make) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);
+ return true;
+}
+
+bool ExifUtilsImpl::setModel(const std::string& model) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);
+ return true;
+}
+
+void ExifUtilsImpl::reset() {
+ destroyApp1();
+ if (exif_data_) {
+ /*
+ * Since we decided to ignore the original APP1, we are sure that there is
+ * no thumbnail allocated by libexif. |exif_data_->data| is actually
+ * allocated by JpegCompressor. sets |exif_data_->data| to nullptr to
+ * prevent exif_data_unref() destroy it incorrectly.
+ */
+ exif_data_->data = nullptr;
+ exif_data_->size = 0;
+ exif_data_unref(exif_data_);
+ exif_data_ = nullptr;
+ }
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ uint64_t components,
+ unsigned int size) {
+ // Remove old entry if exists.
+ exif_content_remove_entry(exif_data_->ifd[ifd],
+ exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ ExifMem* mem = exif_mem_new_default();
+ if (!mem) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+ void* tmpBuffer = exif_mem_alloc(mem, size);
+ if (!tmpBuffer) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+
+ entry->data = static_cast<unsigned char*>(tmpBuffer);
+ entry->tag = tag;
+ entry->format = format;
+ entry->components = components;
+ entry->size = size;
+
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_mem_unref(mem);
+
+ return entry;
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addEntry(ExifIfd ifd, ExifTag tag) {
+ std::unique_ptr<ExifEntry> entry(exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ if (entry) {
+ // exif_content_get_entry() won't ref the entry, so we ref here.
+ exif_entry_ref(entry.get());
+ return entry;
+ }
+ entry.reset(exif_entry_new());
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ entry->tag = tag;
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_entry_initialize(entry.get(), tag);
+ return entry;
+}
+
+bool ExifUtilsImpl::setShort(ExifIfd ifd,
+ ExifTag tag,
+ uint16_t value,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setLong(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t value,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_long(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setRational(ExifIfd ifd,
+ ExifTag tag,
+ uint32_t numerator,
+ uint32_t denominator,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setSRational(ExifIfd ifd,
+ ExifTag tag,
+ int32_t numerator,
+ int32_t denominator,
+ const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setString(ExifIfd ifd,
+ ExifTag tag,
+ ExifFormat format,
+ const std::string& buffer,
+ const std::string& msg) {
+ size_t entry_size = buffer.length();
+ // Since the exif format is undefined, NULL termination is not necessary.
+ if (format == EXIF_FORMAT_ASCII) {
+ entry_size++;
+ }
+ std::unique_ptr<ExifEntry> entry =
+ addVariableLengthEntry(ifd, tag, format, entry_size, entry_size);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ memcpy(entry->data, buffer.c_str(), entry_size);
+ return true;
+}
+
+void ExifUtilsImpl::destroyApp1() {
+ /*
+ * Since there is no API to access ExifMem in ExifData->priv, we use free
+ * here, which is the default free function in libexif. See
+ * exif_data_save_data() for detail.
+ */
+ free(app1_buffer_);
+ app1_buffer_ = nullptr;
+ app1_length_ = 0;
+}
+
+bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight) {
+ // How precise the float-to-rational conversion for EXIF tags would be.
+ constexpr int kRationalPrecision = 10000;
+ if (!setImageWidth(imageWidth) ||
+ !setImageHeight(imageHeight)) {
+ ALOGE("%s: setting image resolution failed.", __FUNCTION__);
+ return false;
+ }
+
+ struct timespec tp;
+ struct tm time_info;
+ bool time_available = clock_gettime(CLOCK_REALTIME, &tp) != -1;
+ localtime_r(&tp.tv_sec, &time_info);
+ if (!setDateTime(time_info)) {
+ ALOGE("%s: setting data time failed.", __FUNCTION__);
+ return false;
+ }
+
+ float focal_length;
+ camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
+ if (entry.count) {
+ focal_length = entry.data.f[0];
+ } else {
+ ALOGE("%s: Cannot find focal length in metadata.", __FUNCTION__);
+ return false;
+ }
+ if (!setFocalLength(
+ static_cast<uint32_t>(focal_length * kRationalPrecision),
+ kRationalPrecision)) {
+ ALOGE("%s: setting focal length failed.", __FUNCTION__);
+ return false;
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES);
+ if (entry.count < 3) {
+ ALOGE("%s: Gps coordinates in metadata is not complete.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLatitude(entry.data.d[0])) {
+ ALOGE("%s: setting gps latitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLongitude(entry.data.d[1])) {
+ ALOGE("%s: setting gps longitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsAltitude(entry.data.d[2])) {
+ ALOGE("%s: setting gps altitude failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD);
+ std::string method_str(reinterpret_cast<const char*>(entry.data.u8));
+ if (!setGpsProcessingMethod(method_str)) {
+ ALOGE("%s: setting gps processing method failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP);
+ time_t timestamp = static_cast<time_t>(entry.data.i64[0]);
+ if (gmtime_r(×tamp, &time_info)) {
+ if (!setGpsTimestamp(time_info)) {
+ ALOGE("%s: setting gps timestamp failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Time tranformation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_ORIENTATION)) {
+ entry = metadata.find(ANDROID_JPEG_ORIENTATION);
+ if (!setOrientation(entry.data.i32[0])) {
+ ALOGE("%s: setting orientation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
+ entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME);
+ // int64_t of nanoseconds
+ if (!setExposureTime(entry.data.i64[0],1000000000u)) {
+ ALOGE("%s: setting exposure time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_LENS_APERTURE)) {
+ const int kAperturePrecision = 10000;
+ entry = metadata.find(ANDROID_LENS_APERTURE);
+ if (!setFNumber(entry.data.f[0] * kAperturePrecision,
+ kAperturePrecision)) {
+ ALOGE("%s: setting F number failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) {
+ entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
+ if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) {
+ const uint32_t kNoFlashFunction = 0x20;
+ if (!setFlash(kNoFlashFunction)) {
+ ALOGE("%s: setting flash failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) {
+ entry = metadata.find(ANDROID_CONTROL_AWB_MODE);
+ if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) {
+ const uint16_t kAutoWhiteBalance = 0;
+ if (!setWhiteBalance(kAutoWhiteBalance)) {
+ ALOGE("%s: setting white balance failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (time_available) {
+ char str[4];
+ if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) {
+ ALOGE("%s: Subsec is invalid: %ld", __FUNCTION__, tp.tv_nsec);
+ return false;
+ }
+ if (!setSubsecTime(std::string(str))) {
+ ALOGE("%s: setting subsec time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
diff --git a/camera/common/1.0/default/HandleImporter.cpp b/camera/common/1.0/default/HandleImporter.cpp
index e9741ef..21706a8 100644
--- a/camera/common/1.0/default/HandleImporter.cpp
+++ b/camera/common/1.0/default/HandleImporter.cpp
@@ -134,6 +134,38 @@
}
}
+void* HandleImporter::lock(
+ buffer_handle_t& buf, uint64_t cpuUsage, size_t size) {
+ Mutex::Autolock lock(mLock);
+ void *ret = 0;
+ IMapper::Rect accessRegion { 0, 0, static_cast<int>(size), 1 };
+
+ if (!mInitialized) {
+ initializeLocked();
+ }
+
+ if (mMapper == nullptr) {
+ ALOGE("%s: mMapper is null!", __FUNCTION__);
+ return ret;
+ }
+
+ hidl_handle acquireFenceHandle;
+ auto buffer = const_cast<native_handle_t*>(buf);
+ mMapper->lock(buffer, cpuUsage, accessRegion, acquireFenceHandle,
+ [&](const auto& tmpError, const auto& tmpPtr) {
+ if (tmpError == MapperError::NONE) {
+ ret = tmpPtr;
+ } else {
+ ALOGE("%s: failed to lock error %d!",
+ __FUNCTION__, tmpError);
+ }
+ });
+
+ ALOGV("%s: ptr %p size: %zu", __FUNCTION__, ret, size);
+ return ret;
+}
+
+
YCbCrLayout HandleImporter::lockYCbCr(
buffer_handle_t& buf, uint64_t cpuUsage,
const IMapper::Rect& accessRegion) {
diff --git a/camera/common/1.0/default/include/Exif.h b/camera/common/1.0/default/include/Exif.h
new file mode 100644
index 0000000..dc31679
--- /dev/null
+++ b/camera/common/1.0/default/include/Exif.h
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
+#define ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
+
+#include "CameraMetadata.h"
+
+namespace android {
+namespace hardware {
+namespace camera {
+namespace common {
+namespace V1_0 {
+namespace helper {
+
+
+// This is based on the original ChromeOS ARC implementation of a V4L2 HAL
+
+// ExifUtils can generate APP1 segment with tags which caller set. ExifUtils can
+// also add a thumbnail in the APP1 segment if thumbnail size is specified.
+// ExifUtils can be reused with different images by calling initialize().
+//
+// Example of using this class :
+// std::unique_ptr<ExifUtils> utils(ExifUtils::Create());
+// utils->initialize();
+// ...
+// // Call ExifUtils functions to set Exif tags.
+// ...
+// utils->GenerateApp1(thumbnail_buffer, thumbnail_size);
+// unsigned int app1Length = utils->GetApp1Length();
+// uint8_t* app1Buffer = new uint8_t[app1Length];
+// memcpy(app1Buffer, utils->GetApp1Buffer(), app1Length);
+class ExifUtils {
+
+ public:
+ virtual ~ExifUtils();
+
+ static ExifUtils* create();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize() = 0;
+
+ // Set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight) = 0;
+
+ // Sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space) = 0;
+
+ // Sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration) = 0;
+
+ // Sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression) = 0;
+
+ // Sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast) = 0;
+
+ // Sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t) = 0;
+
+ // Sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description) = 0;
+
+ // Sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode) = 0;
+
+ // Sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program) = 0;
+
+ // Sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash) = 0;
+
+ // Sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control) = 0;
+
+ // Sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude) = 0;
+
+ // Sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude) = 0;
+
+ // Sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude) = 0;
+
+ // Sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method) = 0;
+
+ // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t) = 0;
+
+ // Sets the height (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length) = 0;
+
+ // Sets the width (number of columns) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width) = 0;
+
+ // Sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings) = 0;
+
+ // Sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source) = 0;
+
+ // Sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode) = 0;
+
+ // Sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation) = 0;
+
+ // Sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit) = 0;
+
+ // Sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation) = 0;
+
+ // Sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type) = 0;
+
+ // Sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness) = 0;
+
+ // Sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time) = 0;
+
+ // Sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance) = 0;
+
+ // Sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning) = 0;
+
+ // Sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make) = 0;
+
+ // Sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model) = 0;
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1(const void* thumbnail_buffer, uint32_t size) = 0;
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer() = 0;
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length() = 0;
+};
+
+
+} // namespace helper
+} // namespace V1_0
+} // namespace common
+} // namespace camera
+} // namespace hardware
+} // namespace android
+
+
+#endif // ANDROID_HARDWARE_INTERFACES_CAMERA_COMMON_1_0_EXIF_H
diff --git a/camera/common/1.0/default/include/HandleImporter.h b/camera/common/1.0/default/include/HandleImporter.h
index 443362d..f9cd9fb 100644
--- a/camera/common/1.0/default/include/HandleImporter.h
+++ b/camera/common/1.0/default/include/HandleImporter.h
@@ -45,6 +45,9 @@
void closeFence(int fd) const;
// Assume caller has done waiting for acquire fences
+ void* lock(buffer_handle_t& buf, uint64_t cpuUsage, size_t size);
+
+ // Assume caller has done waiting for acquire fences
YCbCrLayout lockYCbCr(buffer_handle_t& buf, uint64_t cpuUsage,
const IMapper::Rect& accessRegion);
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index 31b4739..975fb01 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -738,8 +738,14 @@
// Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow.
Return<void> CameraDeviceSession::constructDefaultRequestSettings(
RequestTemplate type, ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
- Status status = initStatus();
CameraMetadata outMetadata;
+ Status status = constructDefaultRequestSettingsRaw( (int) type, &outMetadata);
+ _hidl_cb(status, outMetadata);
+ return Void();
+}
+
+Status CameraDeviceSession::constructDefaultRequestSettingsRaw(int type, CameraMetadata *outMetadata) {
+ Status status = initStatus();
const camera_metadata_t *rawRequest;
if (status == Status::OK) {
ATRACE_BEGIN("camera3->construct_default_request_settings");
@@ -761,15 +767,14 @@
defaultBoost, 1);
const camera_metadata_t *metaBuffer =
mOverridenRequest.getAndLock();
- convertToHidl(metaBuffer, &outMetadata);
+ convertToHidl(metaBuffer, outMetadata);
mOverridenRequest.unlock(metaBuffer);
} else {
- convertToHidl(rawRequest, &outMetadata);
+ convertToHidl(rawRequest, outMetadata);
}
}
}
- _hidl_cb(status, outMetadata);
- return Void();
+ return status;
}
/**
@@ -1193,26 +1198,19 @@
return Void();
}
-/**
- * Static callback forwarding methods from HAL to instance
- */
-void CameraDeviceSession::sProcessCaptureResult(
- const camera3_callback_ops *cb,
- const camera3_capture_result *hal_result) {
- CameraDeviceSession *d =
- const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
-
+void CameraDeviceSession::constructCaptureResult(CaptureResult& result,
+ const camera3_capture_result *hal_result) {
uint32_t frameNumber = hal_result->frame_number;
bool hasInputBuf = (hal_result->input_buffer != nullptr);
size_t numOutputBufs = hal_result->num_output_buffers;
size_t numBufs = numOutputBufs + (hasInputBuf ? 1 : 0);
if (numBufs > 0) {
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
if (hasInputBuf) {
int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
// validate if buffer is inflight
auto key = std::make_pair(streamId, frameNumber);
- if (d->mInflightBuffers.count(key) != 1) {
+ if (mInflightBuffers.count(key) != 1) {
ALOGE("%s: input buffer for stream %d frame %d is not inflight!",
__FUNCTION__, streamId, frameNumber);
return;
@@ -1223,7 +1221,7 @@
int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
// validate if buffer is inflight
auto key = std::make_pair(streamId, frameNumber);
- if (d->mInflightBuffers.count(key) != 1) {
+ if (mInflightBuffers.count(key) != 1) {
ALOGE("%s: output buffer for stream %d frame %d is not inflight!",
__FUNCTION__, streamId, frameNumber);
return;
@@ -1232,64 +1230,63 @@
}
// We don't need to validate/import fences here since we will be passing them to camera service
// within the scope of this function
- CaptureResult result;
result.frameNumber = frameNumber;
result.fmqResultSize = 0;
result.partialResult = hal_result->partial_result;
convertToHidl(hal_result->result, &result.result);
if (nullptr != hal_result->result) {
bool resultOverriden = false;
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
// Derive some new keys for backward compatibility
- if (d->mDerivePostRawSensKey) {
+ if (mDerivePostRawSensKey) {
camera_metadata_ro_entry entry;
if (find_camera_metadata_ro_entry(hal_result->result,
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, &entry) == 0) {
- d->mInflightRawBoostPresent[frameNumber] = true;
+ mInflightRawBoostPresent[frameNumber] = true;
} else {
- auto entry = d->mInflightRawBoostPresent.find(frameNumber);
- if (d->mInflightRawBoostPresent.end() == entry) {
- d->mInflightRawBoostPresent[frameNumber] = false;
+ auto entry = mInflightRawBoostPresent.find(frameNumber);
+ if (mInflightRawBoostPresent.end() == entry) {
+ mInflightRawBoostPresent[frameNumber] = false;
}
}
- if ((hal_result->partial_result == d->mNumPartialResults)) {
- if (!d->mInflightRawBoostPresent[frameNumber]) {
+ if ((hal_result->partial_result == mNumPartialResults)) {
+ if (!mInflightRawBoostPresent[frameNumber]) {
if (!resultOverriden) {
- d->mOverridenResult.clear();
- d->mOverridenResult.append(hal_result->result);
+ mOverridenResult.clear();
+ mOverridenResult.append(hal_result->result);
resultOverriden = true;
}
int32_t defaultBoost[1] = {100};
- d->mOverridenResult.update(
+ mOverridenResult.update(
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
defaultBoost, 1);
}
- d->mInflightRawBoostPresent.erase(frameNumber);
+ mInflightRawBoostPresent.erase(frameNumber);
}
}
- auto entry = d->mInflightAETriggerOverrides.find(frameNumber);
- if (d->mInflightAETriggerOverrides.end() != entry) {
+ auto entry = mInflightAETriggerOverrides.find(frameNumber);
+ if (mInflightAETriggerOverrides.end() != entry) {
if (!resultOverriden) {
- d->mOverridenResult.clear();
- d->mOverridenResult.append(hal_result->result);
+ mOverridenResult.clear();
+ mOverridenResult.append(hal_result->result);
resultOverriden = true;
}
- d->overrideResultForPrecaptureCancelLocked(entry->second,
- &d->mOverridenResult);
- if (hal_result->partial_result == d->mNumPartialResults) {
- d->mInflightAETriggerOverrides.erase(frameNumber);
+ overrideResultForPrecaptureCancelLocked(entry->second,
+ &mOverridenResult);
+ if (hal_result->partial_result == mNumPartialResults) {
+ mInflightAETriggerOverrides.erase(frameNumber);
}
}
if (resultOverriden) {
const camera_metadata_t *metaBuffer =
- d->mOverridenResult.getAndLock();
+ mOverridenResult.getAndLock();
convertToHidl(metaBuffer, &result.result);
- d->mOverridenResult.unlock(metaBuffer);
+ mOverridenResult.unlock(metaBuffer);
}
}
if (hasInputBuf) {
@@ -1330,24 +1327,38 @@
// configure_streams right after the processCaptureResult call so we need to finish
// updating inflight queues first
if (numBufs > 0) {
- Mutex::Autolock _l(d->mInflightLock);
+ Mutex::Autolock _l(mInflightLock);
if (hasInputBuf) {
int streamId = static_cast<Camera3Stream*>(hal_result->input_buffer->stream)->mId;
auto key = std::make_pair(streamId, frameNumber);
- d->mInflightBuffers.erase(key);
+ mInflightBuffers.erase(key);
}
for (size_t i = 0; i < numOutputBufs; i++) {
int streamId = static_cast<Camera3Stream*>(hal_result->output_buffers[i].stream)->mId;
auto key = std::make_pair(streamId, frameNumber);
- d->mInflightBuffers.erase(key);
+ mInflightBuffers.erase(key);
}
- if (d->mInflightBuffers.empty()) {
+ if (mInflightBuffers.empty()) {
ALOGV("%s: inflight buffer queue is now empty!", __FUNCTION__);
}
}
+}
+
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+void CameraDeviceSession::sProcessCaptureResult(
+ const camera3_callback_ops *cb,
+ const camera3_capture_result *hal_result) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+ CaptureResult result;
+ d->constructCaptureResult(result, hal_result);
+
d->mResultBatcher.processCaptureResult(result);
}
diff --git a/camera/device/3.2/default/CameraDeviceSession.h b/camera/device/3.2/default/CameraDeviceSession.h
index 0048ef4..61db671 100644
--- a/camera/device/3.2/default/CameraDeviceSession.h
+++ b/camera/device/3.2/default/CameraDeviceSession.h
@@ -112,7 +112,9 @@
Return<Status> flush();
Return<void> close();
- //Helper methods
+ // Helper methods
+ Status constructDefaultRequestSettingsRaw(int type, CameraMetadata *outMetadata);
+
bool preProcessConfigurationLocked(const StreamConfiguration& requestedConfiguration,
camera3_stream_configuration_t *stream_list /*out*/,
hidl_vec<camera3_stream_t*> *streams /*out*/);
@@ -188,7 +190,7 @@
void notify(NotifyMsg& msg);
void processCaptureResult(CaptureResult& result);
- private:
+ protected:
struct InflightBatch {
// Protect access to entire struct. Acquire this lock before read/write any data or
// calling any methods. processCaptureResult and notify will compete for this lock
@@ -233,7 +235,6 @@
bool mRemoved = false;
};
- static const int NOT_BATCHED = -1;
// Get the batch index and pointer to InflightBatch (nullptrt if the frame is not batched)
// Caller must acquire the InflightBatch::mLock before accessing the InflightBatch
@@ -243,6 +244,16 @@
// This method will hold ResultBatcher::mLock briefly
std::pair<int, std::shared_ptr<InflightBatch>> getBatch(uint32_t frameNumber);
+ static const int NOT_BATCHED = -1;
+
+ // move/push function avoids "hidl_handle& operator=(hidl_handle&)", which clones native
+ // handle
+ void moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst);
+ void pushStreamBuffer(StreamBuffer&& src, std::vector<StreamBuffer>& dst);
+
+ void sendBatchMetadataLocked(
+ std::shared_ptr<InflightBatch> batch, uint32_t lastPartialResultIdx);
+
// Check if the first batch in mInflightBatches is ready to be removed, and remove it if so
// This method will hold ResultBatcher::mLock briefly
void checkAndRemoveFirstBatch();
@@ -255,9 +266,7 @@
// send buffers for specified streams
void sendBatchBuffersLocked(
std::shared_ptr<InflightBatch> batch, const std::vector<int>& streams);
- void sendBatchMetadataLocked(
- std::shared_ptr<InflightBatch> batch, uint32_t lastPartialResultIdx);
- // End of sendXXXX methods
+ // End of sendXXXX methods
// helper methods
void freeReleaseFences(hidl_vec<CaptureResult>&);
@@ -265,11 +274,6 @@
void processOneCaptureResult(CaptureResult& result);
void invokeProcessCaptureResultCallback(hidl_vec<CaptureResult> &results, bool tryWriteFmq);
- // move/push function avoids "hidl_handle& operator=(hidl_handle&)", which clones native
- // handle
- void moveStreamBuffer(StreamBuffer&& src, StreamBuffer& dst);
- void pushStreamBuffer(StreamBuffer&& src, std::vector<StreamBuffer>& dst);
-
// Protect access to mInflightBatches, mNumPartialResults and mStreamsToBatch
// processCaptureRequest, processCaptureResult, notify will compete for this lock
// Do NOT issue HIDL IPCs while holding this lock (except when HAL reports error)
@@ -323,6 +327,8 @@
static callbacks_process_capture_result_t sProcessCaptureResult;
static callbacks_notify_t sNotify;
+ void constructCaptureResult(CaptureResult& result,
+ const camera3_capture_result *hal_result);
private:
struct TrampolineSessionInterface_3_2 : public ICameraDeviceSession {
diff --git a/camera/device/3.4/Android.bp b/camera/device/3.4/Android.bp
index 67b79b9..87acd25 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"types.hal",
+ "ICameraDeviceCallback.hal",
"ICameraDeviceSession.hal",
],
interfaces: [
@@ -18,8 +19,13 @@
"android.hidl.base@1.0",
],
types: [
+ "CaptureRequest",
+ "CaptureResult",
"HalStream",
"HalStreamConfiguration",
+ "PhysicalCameraMetadata",
+ "PhysicalCameraSetting",
+ "RequestTemplate",
"Stream",
"StreamConfiguration",
],
diff --git a/camera/device/3.4/ICameraDeviceCallback.hal b/camera/device/3.4/ICameraDeviceCallback.hal
new file mode 100644
index 0000000..8ce8d4b
--- /dev/null
+++ b/camera/device/3.4/ICameraDeviceCallback.hal
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera.device@3.4;
+
+import @3.2::ICameraDeviceCallback;
+
+/**
+ *
+ * Callback methods for the HAL to call into the framework.
+ *
+ * These methods are used to return metadata and image buffers for a completed
+ * or failed captures, and to notify the framework of asynchronous events such
+ * as errors.
+ *
+ * The framework must not call back into the HAL from within these callbacks,
+ * and these calls must not block for extended periods.
+ *
+ */
+interface ICameraDeviceCallback extends @3.2::ICameraDeviceCallback {
+ /**
+ * processCaptureResult_3_4:
+ *
+ * Identical to @3.2::ICameraDeviceCallback.processCaptureResult, except
+ * that it takes a list of @3.4::CaptureResult, which could contain
+ * physical camera metadata for logical multi-camera.
+ *
+ */
+ processCaptureResult_3_4(vec<@3.4::CaptureResult> results);
+};
diff --git a/camera/device/3.4/ICameraDeviceSession.hal b/camera/device/3.4/ICameraDeviceSession.hal
index 4ce749d..7afcf94 100644
--- a/camera/device/3.4/ICameraDeviceSession.hal
+++ b/camera/device/3.4/ICameraDeviceSession.hal
@@ -17,6 +17,7 @@
package android.hardware.camera.device@3.4;
import android.hardware.camera.common@1.0::Status;
+import @3.2::CameraMetadata;
import @3.3::ICameraDeviceSession;
import @3.3::HalStreamConfiguration;
import @3.2::BufferCache;
@@ -30,6 +31,43 @@
interface ICameraDeviceSession extends @3.3::ICameraDeviceSession {
/**
+ * constructDefaultRequestSettings_3_4:
+ *
+ * Create capture settings for standard camera use cases. Supports the
+ * new template enums added in @3.4.
+ *
+ * The device must return a settings buffer that is configured to meet the
+ * requested use case, which must be one of the CAMERA3_TEMPLATE_*
+ * enums. All request control fields must be included.
+ *
+ * Performance requirements:
+ *
+ * This must be a non-blocking call. The HAL should return from this call
+ * in 1ms, and must return from this call in 5ms.
+ *
+ * Return values:
+ * @return status Status code for the operation, one of:
+ * OK:
+ * On a successful construction of default settings.
+ * INTERNAL_ERROR:
+ * An unexpected internal error occurred, and the default settings
+ * are not available.
+ * ILLEGAL_ARGUMENT:
+ * The camera HAL does not support the input template type
+ * CAMERA_DISCONNECTED:
+ * An external camera device has been disconnected, and is no longer
+ * available. This camera device interface is now stale, and a new
+ * instance must be acquired if the device is reconnected. All
+ * subsequent calls on this interface must return
+ * CAMERA_DISCONNECTED.
+ * @return requestTemplate The default capture request settings for the requested
+ * use case, or an empty metadata structure if status is not OK.
+ *
+ */
+ constructDefaultRequestSettings_3_4(RequestTemplate type) generates
+ (Status status, @3.2::CameraMetadata requestTemplate);
+
+ /**
* configureStreams_3_4:
*
* Identical to @3.3::ICameraDeviceSession.configureStreams, except that:
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index 61ac244..a936dae 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -34,7 +34,7 @@
srcs: [
"CameraDevice.cpp",
"CameraDeviceSession.cpp",
- "convert.cpp",
+ "convert.cpp"
],
shared_libs: [
"libhidlbase",
@@ -89,6 +89,8 @@
"libfmq",
"libsync",
"libyuv",
+ "libjpeg",
+ "libexif",
],
static_libs: [
"android.hardware.camera.common@1.0-helper",
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index c8d33eb..f6c6b2b 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -34,18 +34,54 @@
camera3_device_t* device,
const camera_metadata_t* deviceInfo,
const sp<V3_2::ICameraDeviceCallback>& callback) :
- V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback) {
+ V3_3::implementation::CameraDeviceSession(device, deviceInfo, callback),
+ mResultBatcher_3_4(callback) {
+
+ mHasCallback_3_4 = false;
+
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ sp<ICameraDeviceCallback> callback3_4 = castResult;
+ if (callback3_4 != nullptr) {
+ process_capture_result = sProcessCaptureResult_3_4;
+ notify = sNotify_3_4;
+ mHasCallback_3_4 = true;
+ if (!mInitFail) {
+ mResultBatcher_3_4.setResultMetadataQueue(mResultMetadataQueue);
+ }
+ }
+ }
}
CameraDeviceSession::~CameraDeviceSession() {
}
+Return<void> CameraDeviceSession::constructDefaultRequestSettings_3_4(
+ RequestTemplate type, ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ V3_2::CameraMetadata outMetadata;
+ Status status = constructDefaultRequestSettingsRaw( (int) type, &outMetadata);
+ _hidl_cb(status, outMetadata);
+ return Void();
+}
+
Return<void> CameraDeviceSession::configureStreams_3_4(
const StreamConfiguration& requestedConfiguration,
ICameraDeviceSession::configureStreams_3_4_cb _hidl_cb) {
Status status = initStatus();
HalStreamConfiguration outStreams;
+ // If callback is 3.2, make sure no physical stream is configured
+ if (!mHasCallback_3_4) {
+ for (size_t i = 0; i < requestedConfiguration.streams.size(); i++) {
+ if (requestedConfiguration.streams[i].physicalCameraId.size() > 0) {
+ ALOGE("%s: trying to configureStreams with physical camera id with V3.2 callback",
+ __FUNCTION__);
+ _hidl_cb(Status::INTERNAL_ERROR, outStreams);
+ return Void();
+ }
+ }
+ }
+
// hold the inflight lock for entire configureStreams scope since there must not be any
// inflight request/results during stream configuration.
Mutex::Autolock _l(mInflightLock);
@@ -197,7 +233,7 @@
mVideoStreamIds.push_back(stream.v3_2.id);
}
}
- mResultBatcher.setBatchedStreams(mVideoStreamIds);
+ mResultBatcher_3_4.setBatchedStreams(mVideoStreamIds);
}
Return<void> CameraDeviceSession::processCaptureRequest_3_4(
@@ -216,7 +252,7 @@
}
if (s == Status::OK && requests.size() > 1) {
- mResultBatcher.registerBatch(requests[0].v3_2.frameNumber, requests.size());
+ mResultBatcher_3_4.registerBatch(requests[0].v3_2.frameNumber, requests.size());
}
_hidl_cb(s, numRequestProcessed);
@@ -229,6 +265,14 @@
ALOGE("%s: camera init failed or disconnected", __FUNCTION__);
return status;
}
+ // If callback is 3.2, make sure there are no physical settings.
+ if (!mHasCallback_3_4) {
+ if (request.physicalCameraSettings.size() > 0) {
+ ALOGE("%s: trying to call processCaptureRequest_3_4 with physical camera id "
+ "and V3.2 callback", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
camera3_capture_request_t halRequest;
halRequest.frame_number = request.v3_2.frameNumber;
@@ -399,6 +443,228 @@
return Status::OK;
}
+/**
+ * Static callback forwarding methods from HAL to instance
+ */
+void CameraDeviceSession::sProcessCaptureResult_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_capture_result *hal_result) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+
+ CaptureResult result;
+ d->constructCaptureResult(result.v3_2, hal_result);
+ result.physicalCameraMetadata.resize(hal_result->num_physcam_metadata);
+ for (uint32_t i = 0; i < hal_result->num_physcam_metadata; i++) {
+ std::string physicalId = hal_result->physcam_ids[i];
+ V3_2::CameraMetadata physicalMetadata;
+ V3_2::implementation::convertToHidl(hal_result->physcam_metadata[i], &physicalMetadata);
+ PhysicalCameraMetadata physicalCameraMetadata = {
+ .fmqMetadataSize = 0,
+ .physicalCameraId = physicalId,
+ .metadata = physicalMetadata };
+ result.physicalCameraMetadata[i] = physicalCameraMetadata;
+ }
+ d->mResultBatcher_3_4.processCaptureResult_3_4(result);
+}
+
+void CameraDeviceSession::sNotify_3_4(
+ const camera3_callback_ops *cb,
+ const camera3_notify_msg *msg) {
+ CameraDeviceSession *d =
+ const_cast<CameraDeviceSession*>(static_cast<const CameraDeviceSession*>(cb));
+ V3_2::NotifyMsg hidlMsg;
+ V3_2::implementation::convertToHidl(msg, &hidlMsg);
+
+ if (hidlMsg.type == (V3_2::MsgType) CAMERA3_MSG_ERROR &&
+ hidlMsg.msg.error.errorStreamId != -1) {
+ if (d->mStreamMap.count(hidlMsg.msg.error.errorStreamId) != 1) {
+ ALOGE("%s: unknown stream ID %d reports an error!",
+ __FUNCTION__, hidlMsg.msg.error.errorStreamId);
+ return;
+ }
+ }
+
+ if (static_cast<camera3_msg_type_t>(hidlMsg.type) == CAMERA3_MSG_ERROR) {
+ switch (hidlMsg.msg.error.errorCode) {
+ case V3_2::ErrorCode::ERROR_DEVICE:
+ case V3_2::ErrorCode::ERROR_REQUEST:
+ case V3_2::ErrorCode::ERROR_RESULT: {
+ Mutex::Autolock _l(d->mInflightLock);
+ auto entry = d->mInflightAETriggerOverrides.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightAETriggerOverrides.end() != entry) {
+ d->mInflightAETriggerOverrides.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ auto boostEntry = d->mInflightRawBoostPresent.find(
+ hidlMsg.msg.error.frameNumber);
+ if (d->mInflightRawBoostPresent.end() != boostEntry) {
+ d->mInflightRawBoostPresent.erase(
+ hidlMsg.msg.error.frameNumber);
+ }
+
+ }
+ break;
+ case V3_2::ErrorCode::ERROR_BUFFER:
+ default:
+ break;
+ }
+
+ }
+
+ d->mResultBatcher_3_4.notify(hidlMsg);
+}
+
+CameraDeviceSession::ResultBatcher_3_4::ResultBatcher_3_4(
+ const sp<V3_2::ICameraDeviceCallback>& callback) :
+ V3_3::implementation::CameraDeviceSession::ResultBatcher(callback) {
+ auto castResult = ICameraDeviceCallback::castFrom(callback);
+ if (castResult.isOk()) {
+ mCallback_3_4 = castResult;
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processCaptureResult_3_4(CaptureResult& result) {
+ auto pair = getBatch(result.v3_2.frameNumber);
+ int batchIdx = pair.first;
+ if (batchIdx == NOT_BATCHED) {
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+ std::shared_ptr<InflightBatch> batch = pair.second;
+ {
+ Mutex::Autolock _l(batch->mLock);
+ // Check if the batch is removed (mostly by notify error) before lock was acquired
+ if (batch->mRemoved) {
+ // Fall back to non-batch path
+ processOneCaptureResult_3_4(result);
+ return;
+ }
+
+ // queue metadata
+ if (result.v3_2.result.size() != 0) {
+ // Save a copy of metadata
+ batch->mResultMds[result.v3_2.partialResult].mMds.push_back(
+ std::make_pair(result.v3_2.frameNumber, result.v3_2.result));
+ }
+
+ // queue buffer
+ std::vector<int> filledStreams;
+ std::vector<V3_2::StreamBuffer> nonBatchedBuffers;
+ for (auto& buffer : result.v3_2.outputBuffers) {
+ auto it = batch->mBatchBufs.find(buffer.streamId);
+ if (it != batch->mBatchBufs.end()) {
+ InflightBatch::BufferBatch& bb = it->second;
+ pushStreamBuffer(std::move(buffer), bb.mBuffers);
+ filledStreams.push_back(buffer.streamId);
+ } else {
+ pushStreamBuffer(std::move(buffer), nonBatchedBuffers);
+ }
+ }
+
+ // send non-batched buffers up
+ if (nonBatchedBuffers.size() > 0 || result.v3_2.inputBuffer.streamId != -1) {
+ CaptureResult nonBatchedResult;
+ nonBatchedResult.v3_2.frameNumber = result.v3_2.frameNumber;
+ nonBatchedResult.v3_2.fmqResultSize = 0;
+ nonBatchedResult.v3_2.outputBuffers.resize(nonBatchedBuffers.size());
+ for (size_t i = 0; i < nonBatchedBuffers.size(); i++) {
+ moveStreamBuffer(
+ std::move(nonBatchedBuffers[i]), nonBatchedResult.v3_2.outputBuffers[i]);
+ }
+ moveStreamBuffer(std::move(result.v3_2.inputBuffer), nonBatchedResult.v3_2.inputBuffer);
+ nonBatchedResult.v3_2.partialResult = 0; // 0 for buffer only results
+ processOneCaptureResult_3_4(nonBatchedResult);
+ }
+
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ // Send data up
+ if (result.v3_2.partialResult > 0) {
+ sendBatchMetadataLocked(batch, result.v3_2.partialResult);
+ }
+ // send buffer up
+ if (filledStreams.size() > 0) {
+ sendBatchBuffersLocked(batch, filledStreams);
+ }
+ }
+ } // end of batch lock scope
+
+ // see if the batch is complete
+ if (result.v3_2.frameNumber == batch->mLastFrame) {
+ checkAndRemoveFirstBatch();
+ }
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::processOneCaptureResult_3_4(CaptureResult& result) {
+ hidl_vec<CaptureResult> results;
+ results.resize(1);
+ results[0] = std::move(result);
+ invokeProcessCaptureResultCallback_3_4(results, /* tryWriteFmq */true);
+ freeReleaseFences_3_4(results);
+ return;
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::invokeProcessCaptureResultCallback_3_4(
+ hidl_vec<CaptureResult> &results, bool tryWriteFmq) {
+ if (mProcessCaptureResultLock.tryLock() != OK) {
+ ALOGV("%s: previous call is not finished! waiting 1s...", __FUNCTION__);
+ if (mProcessCaptureResultLock.timedLock(1000000000 /* 1s */) != OK) {
+ ALOGE("%s: cannot acquire lock in 1s, cannot proceed",
+ __FUNCTION__);
+ return;
+ }
+ }
+ if (tryWriteFmq && mResultMetadataQueue->availableToWrite() > 0) {
+ for (CaptureResult &result : results) {
+ if (result.v3_2.result.size() > 0) {
+ if (mResultMetadataQueue->write(result.v3_2.result.data(),
+ result.v3_2.result.size())) {
+ result.v3_2.fmqResultSize = result.v3_2.result.size();
+ result.v3_2.result.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ result.v3_2.fmqResultSize = 0;
+ }
+ }
+
+ for (auto& onePhysMetadata : result.physicalCameraMetadata) {
+ if (mResultMetadataQueue->write(onePhysMetadata.metadata.data(),
+ onePhysMetadata.metadata.size())) {
+ onePhysMetadata.fmqMetadataSize = onePhysMetadata.metadata.size();
+ onePhysMetadata.metadata.resize(0);
+ } else {
+ ALOGW("%s: couldn't utilize fmq, fall back to hwbinder", __FUNCTION__);
+ onePhysMetadata.fmqMetadataSize = 0;
+ }
+ }
+ }
+ }
+ mCallback_3_4->processCaptureResult_3_4(results);
+ mProcessCaptureResultLock.unlock();
+}
+
+void CameraDeviceSession::ResultBatcher_3_4::freeReleaseFences_3_4(hidl_vec<CaptureResult>& results) {
+ for (auto& result : results) {
+ if (result.v3_2.inputBuffer.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ result.v3_2.inputBuffer.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ for (auto& buf : result.v3_2.outputBuffers) {
+ if (buf.releaseFence.getNativeHandle() != nullptr) {
+ native_handle_t* handle = const_cast<native_handle_t*>(
+ buf.releaseFence.getNativeHandle());
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ }
+ }
+ return;
+}
+
} // namespace implementation
} // namespace V3_4
} // namespace device
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index 4ad1768..e6e0ae3 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -47,7 +47,7 @@
// Also make sure that can be done without editing source code
// TODO: b/72261675: make it dynamic since this affects memory usage
-const int kMaxJpegSize = {13 * 1024 * 1024}; // 13MB
+const int kMaxJpegSize = {5 * 1024 * 1024}; // 5MB
} // anonymous namespace
ExternalCameraDevice::ExternalCameraDevice(const std::string& cameraId) :
@@ -227,8 +227,7 @@
status_t ExternalCameraDevice::initDefaultCharsKeys(
::android::hardware::camera::common::V1_0::helper::CameraMetadata* metadata) {
- // TODO: changed to HARDWARELEVEL_EXTERNAL later
- const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+ const uint8_t hardware_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL;
UPDATE(ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &hardware_level, 1);
// android.colorCorrection
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 9589782..ff55489 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -30,6 +30,9 @@
#define HAVE_JPEG // required for libyuv.h to export MJPEG decode APIs
#include <libyuv.h>
+#include <jpeglib.h>
+
+
namespace android {
namespace hardware {
namespace camera {
@@ -57,8 +60,9 @@
HandleImporter ExternalCameraDeviceSession::sHandleImporter;
bool isAspectRatioClose(float ar1, float ar2) {
- const float kAspectRatioMatchThres = 0.01f; // This threshold is good enough to distinguish
+ const float kAspectRatioMatchThres = 0.025f; // This threshold is good enough to distinguish
// 4:3/16:9/20:9
+ // 1.33 / 1.78 / 2
return (std::abs(ar1 - ar2) < kAspectRatioMatchThres);
}
@@ -72,7 +76,9 @@
mV4l2Fd(std::move(v4l2Fd)),
mSupportedFormats(sortFormats(supportedFormats)),
mCroppingType(initCroppingType(mSupportedFormats)),
- mOutputThread(new OutputThread(this, mCroppingType)) {
+ mOutputThread(new OutputThread(this, mCroppingType)),
+ mMaxThumbResolution(getMaxThumbResolution()),
+ mMaxJpegResolution(getMaxJpegResolution()) {
mInitFail = initialize();
}
@@ -93,8 +99,8 @@
const std::vector<SupportedV4L2Format>& sortedFmts) {
const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
float maxSizeAr = ASPECT_RATIO(maxSize);
- float minAr = kMinAspectRatio;
- float maxAr = kMaxAspectRatio;
+ float minAr = kMaxAspectRatio;
+ float maxAr = kMinAspectRatio;
for (const auto& fmt : sortedFmts) {
float ar = ASPECT_RATIO(fmt);
if (ar < minAr) {
@@ -178,33 +184,53 @@
}
Return<void> ExternalCameraDeviceSession::constructDefaultRequestSettings(
+ V3_2::RequestTemplate type,
+ V3_2::ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ V3_2::CameraMetadata outMetadata;
+ Status status = constructDefaultRequestSettingsRaw(
+ static_cast<RequestTemplate>(type), &outMetadata);
+ _hidl_cb(status, outMetadata);
+ return Void();
+}
+
+Return<void> ExternalCameraDeviceSession::constructDefaultRequestSettings_3_4(
RequestTemplate type,
- ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) {
+ V3_2::CameraMetadata outMetadata;
+ Status status = constructDefaultRequestSettingsRaw(type, &outMetadata);
+ _hidl_cb(status, outMetadata);
+ return Void();
+}
+
+Status ExternalCameraDeviceSession::constructDefaultRequestSettingsRaw(RequestTemplate type,
+ V3_2::CameraMetadata *outMetadata) {
CameraMetadata emptyMd;
Status status = initStatus();
if (status != Status::OK) {
- _hidl_cb(status, emptyMd);
- return Void();
+ return status;
}
switch (type) {
case RequestTemplate::PREVIEW:
case RequestTemplate::STILL_CAPTURE:
case RequestTemplate::VIDEO_RECORD:
- case RequestTemplate::VIDEO_SNAPSHOT:
- _hidl_cb(Status::OK, mDefaultRequests[static_cast<int>(type)]);
+ case RequestTemplate::VIDEO_SNAPSHOT: {
+ *outMetadata = mDefaultRequests[type];
break;
+ }
case RequestTemplate::MANUAL:
case RequestTemplate::ZERO_SHUTTER_LAG:
- // Don't support MANUAL or ZSL template
- _hidl_cb(Status::ILLEGAL_ARGUMENT, emptyMd);
+ case RequestTemplate::MOTION_TRACKING_PREVIEW:
+ case RequestTemplate::MOTION_TRACKING_BEST:
+ // Don't support MANUAL, ZSL, MOTION_TRACKING_* templates
+ status = Status::ILLEGAL_ARGUMENT;
break;
default:
ALOGE("%s: unknown request template type %d", __FUNCTION__, static_cast<int>(type));
- _hidl_cb(Status::ILLEGAL_ARGUMENT, emptyMd);
+ status = Status::ILLEGAL_ARGUMENT;
break;
}
- return Void();
+ return status;
}
Return<void> ExternalCameraDeviceSession::configureStreams(
@@ -704,11 +730,24 @@
ALOGE("%s: out is null", __FUNCTION__);
return -1;
}
+
uint32_t inW = inSize.width;
uint32_t inH = inSize.height;
uint32_t outW = outSize.width;
uint32_t outH = outSize.height;
+ // Handle special case where aspect ratio is close to input but scaled
+ // dimension is slightly larger than input
+ float arIn = ASPECT_RATIO(inSize);
+ float arOut = ASPECT_RATIO(outSize);
+ if (isAspectRatioClose(arIn, arOut)) {
+ out->left = 0;
+ out->top = 0;
+ out->width = inW;
+ out->height = inH;
+ return 0;
+ }
+
if (ct == VERTICAL) {
uint64_t scaledOutH = static_cast<uint64_t>(outH) * inW / outW;
if (scaledOutH > inH) {
@@ -745,9 +784,9 @@
}
int ExternalCameraDeviceSession::OutputThread::cropAndScaleLocked(
- sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf, YCbCrLayout* out) {
+ sp<AllocatedFrame>& in, const Size& outSz, YCbCrLayout* out) {
Size inSz = {in->mWidth, in->mHeight};
- Size outSz = {halBuf.width, halBuf.height};
+
int ret;
if (inSz == outSz) {
ret = in->getLayout(out);
@@ -835,6 +874,152 @@
return 0;
}
+
+int ExternalCameraDeviceSession::OutputThread::cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size &outSz, YCbCrLayout* out) {
+ Size inSz {in->mWidth, in->mHeight};
+
+ if ((outSz.width * outSz.height) >
+ (mYu12ThumbFrame->mWidth * mYu12ThumbFrame->mHeight)) {
+ ALOGE("%s: Requested thumbnail size too big (%d,%d) > (%d,%d)",
+ __FUNCTION__, outSz.width, outSz.height,
+ mYu12ThumbFrame->mWidth, mYu12ThumbFrame->mHeight);
+ return -1;
+ }
+
+ int ret;
+
+ /* This will crop-and-zoom the input YUV frame to the thumbnail size
+ * Based on the following logic:
+ * 1) Square pixels come in, square pixels come out, therefore single
+ * scale factor is computed to either make input bigger or smaller
+ * depending on if we are upscaling or downscaling
+ * 2) That single scale factor would either make height too tall or width
+ * too wide so we need to crop the input either horizontally or vertically
+ * but not both
+ */
+
+ /* Convert the input and output dimensions into floats for ease of math */
+ float fWin = static_cast<float>(inSz.width);
+ float fHin = static_cast<float>(inSz.height);
+ float fWout = static_cast<float>(outSz.width);
+ float fHout = static_cast<float>(outSz.height);
+
+ /* Compute the one scale factor from (1) above, it will be the smaller of
+ * the two possibilities. */
+ float scaleFactor = std::min( fHin / fHout, fWin / fWout );
+
+ /* Since we are crop-and-zooming (as opposed to letter/pillar boxing) we can
+ * simply multiply the output by our scaleFactor to get the cropped input
+ * size. Note that at least one of {fWcrop, fHcrop} is going to wind up
+ * being {fWin, fHin} respectively because fHout or fWout cancels out the
+ * scaleFactor calculation above.
+ *
+ * Specifically:
+ * if ( fHin / fHout ) < ( fWin / fWout ) we crop the sides off
+ * input, in which case
+ * scaleFactor = fHin / fHout
+ * fWcrop = fHin / fHout * fWout
+ * fHcrop = fHin
+ *
+ * Note that fWcrop <= fWin ( because ( fHin / fHout ) * fWout < fWin, which
+ * is just the inequality above with both sides multiplied by fWout
+ *
+ * on the other hand if ( fWin / fWout ) < ( fHin / fHout) we crop the top
+ * and the bottom off of input, and
+ * scaleFactor = fWin / fWout
+ * fWcrop = fWin
+ * fHCrop = fWin / fWout * fHout
+ */
+ float fWcrop = scaleFactor * fWout;
+ float fHcrop = scaleFactor * fHout;
+
+ /* Convert to integer and truncate to an even number */
+ Size cropSz = { 2*static_cast<uint32_t>(fWcrop/2.0f),
+ 2*static_cast<uint32_t>(fHcrop/2.0f) };
+
+ /* Convert to a centered rectange with even top/left */
+ IMapper::Rect inputCrop {
+ 2*static_cast<int32_t>((inSz.width - cropSz.width)/4),
+ 2*static_cast<int32_t>((inSz.height - cropSz.height)/4),
+ static_cast<int32_t>(cropSz.width),
+ static_cast<int32_t>(cropSz.height) };
+
+ if ((inputCrop.top < 0) ||
+ (inputCrop.top >= static_cast<int32_t>(inSz.height)) ||
+ (inputCrop.left < 0) ||
+ (inputCrop.left >= static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.width <= 0) ||
+ (inputCrop.width + inputCrop.left > static_cast<int32_t>(inSz.width)) ||
+ (inputCrop.height <= 0) ||
+ (inputCrop.height + inputCrop.top > static_cast<int32_t>(inSz.height)))
+ {
+ ALOGE("%s: came up with really wrong crop rectangle",__FUNCTION__);
+ ALOGE("%s: input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return -1;
+ }
+
+ YCbCrLayout inputLayout;
+ ret = in->getCroppedLayout(inputCrop, &inputLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to crop input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGE("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+ return ret;
+ }
+ ALOGV("%s: crop input layout %dx%d to for output size %dx%d",
+ __FUNCTION__, inSz.width, inSz.height, outSz.width, outSz.height);
+ ALOGV("%s: computed input crop +%d,+%d %dx%d",
+ __FUNCTION__, inputCrop.left, inputCrop.top,
+ inputCrop.width, inputCrop.height);
+
+
+ // Scale
+ YCbCrLayout outFullLayout;
+
+ ret = mYu12ThumbFrame->getLayout(&outFullLayout);
+ if (ret != 0) {
+ ALOGE("%s: failed to get output buffer layout", __FUNCTION__);
+ return ret;
+ }
+
+
+ ret = libyuv::I420Scale(
+ static_cast<uint8_t*>(inputLayout.y),
+ inputLayout.yStride,
+ static_cast<uint8_t*>(inputLayout.cb),
+ inputLayout.cStride,
+ static_cast<uint8_t*>(inputLayout.cr),
+ inputLayout.cStride,
+ inputCrop.width,
+ inputCrop.height,
+ static_cast<uint8_t*>(outFullLayout.y),
+ outFullLayout.yStride,
+ static_cast<uint8_t*>(outFullLayout.cb),
+ outFullLayout.cStride,
+ static_cast<uint8_t*>(outFullLayout.cr),
+ outFullLayout.cStride,
+ outSz.width,
+ outSz.height,
+ libyuv::FilterMode::kFilterNone);
+
+ if (ret != 0) {
+ ALOGE("%s: failed to scale buffer from %dx%d to %dx%d. Ret %d",
+ __FUNCTION__, inputCrop.width, inputCrop.height,
+ outSz.width, outSz.height, ret);
+ return ret;
+ }
+
+ *out = outFullLayout;
+ return 0;
+}
+
int ExternalCameraDeviceSession::OutputThread::formatConvertLocked(
const YCbCrLayout& in, const YCbCrLayout& out, Size sz, uint32_t format) {
int ret = 0;
@@ -917,6 +1102,436 @@
return 0;
}
+int ExternalCameraDeviceSession::OutputThread::encodeJpegYU12(
+ const Size & inSz, const YCbCrLayout& inLayout,
+ int jpegQuality, const void *app1Buffer, size_t app1Size,
+ void *out, const size_t maxOutSize, size_t &actualCodeSize)
+{
+ /* libjpeg is a C library so we use C-style "inheritance" by
+ * putting libjpeg's jpeg_destination_mgr first in our custom
+ * struct. This allows us to cast jpeg_destination_mgr* to
+ * CustomJpegDestMgr* when we get it passed to us in a callback */
+ struct CustomJpegDestMgr {
+ struct jpeg_destination_mgr mgr;
+ JOCTET *mBuffer;
+ size_t mBufferSize;
+ size_t mEncodedSize;
+ bool mSuccess;
+ } dmgr;
+
+ jpeg_compress_struct cinfo = {};
+ jpeg_error_mgr jerr;
+
+ /* Initialize error handling with standard callbacks, but
+ * then override output_message (to print to ALOG) and
+ * error_exit to set a flag and print a message instead
+ * of killing the whole process */
+ cinfo.err = jpeg_std_error(&jerr);
+
+ cinfo.err->output_message = [](j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message)(cinfo, buffer);
+ ALOGE("libjpeg error: %s", buffer);
+ };
+ cinfo.err->error_exit = [](j_common_ptr cinfo) {
+ (*cinfo->err->output_message)(cinfo);
+ if(cinfo->client_data) {
+ auto & dmgr =
+ *reinterpret_cast<CustomJpegDestMgr*>(cinfo->client_data);
+ dmgr.mSuccess = false;
+ }
+ };
+ /* Now that we initialized some callbacks, let's create our compressor */
+ jpeg_create_compress(&cinfo);
+
+ /* Initialize our destination manager */
+ dmgr.mBuffer = static_cast<JOCTET*>(out);
+ dmgr.mBufferSize = maxOutSize;
+ dmgr.mEncodedSize = 0;
+ dmgr.mSuccess = true;
+ cinfo.client_data = static_cast<void*>(&dmgr);
+
+ /* These lambdas become C-style function pointers and as per C++11 spec
+ * may not capture anything */
+ dmgr.mgr.init_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mgr.next_output_byte = dmgr.mBuffer;
+ dmgr.mgr.free_in_buffer = dmgr.mBufferSize;
+ ALOGV("%s:%d jpeg start: %p [%zu]",
+ __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
+ };
+
+ dmgr.mgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
+ ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
+ return 0;
+ };
+
+ dmgr.mgr.term_destination = [](j_compress_ptr cinfo) {
+ auto & dmgr = reinterpret_cast<CustomJpegDestMgr&>(*cinfo->dest);
+ dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.mgr.free_in_buffer;
+ ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
+ };
+ cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
+
+ /* We are going to be using JPEG in raw data mode, so we are passing
+ * straight subsampled planar YCbCr and it will not touch our pixel
+ * data or do any scaling or anything */
+ cinfo.image_width = inSz.width;
+ cinfo.image_height = inSz.height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_YCbCr;
+
+ /* Initialize defaults and then override what we want */
+ jpeg_set_defaults(&cinfo);
+
+ jpeg_set_quality(&cinfo, jpegQuality, 1);
+ jpeg_set_colorspace(&cinfo, JCS_YCbCr);
+ cinfo.raw_data_in = 1;
+ cinfo.dct_method = JDCT_IFAST;
+
+ /* Configure sampling factors. The sampling factor is JPEG subsampling 420
+ * because the source format is YUV420. Note that libjpeg sampling factors
+ * are... a little weird. Sampling of Y=2,U=1,V=1 means there is 1 U and
+ * 1 V value for each 2 Y values */
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+
+ /* Let's not hardcode YUV420 in 6 places... 5 was enough */
+ int maxVSampFactor = std::max( {
+ cinfo.comp_info[0].v_samp_factor,
+ cinfo.comp_info[1].v_samp_factor,
+ cinfo.comp_info[2].v_samp_factor
+ });
+ int cVSubSampling = cinfo.comp_info[0].v_samp_factor /
+ cinfo.comp_info[1].v_samp_factor;
+
+ /* Start the compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Compute our macroblock height, so we can pad our input to be vertically
+ * macroblock aligned.
+ * TODO: Does it need to be horizontally MCU aligned too? */
+
+ size_t mcuV = DCTSIZE*maxVSampFactor;
+ size_t paddedHeight = mcuV * ((inSz.height + mcuV - 1) / mcuV);
+
+ /* libjpeg uses arrays of row pointers, which makes it really easy to pad
+ * data vertically (unfortunately doesn't help horizontally) */
+ std::vector<JSAMPROW> yLines (paddedHeight);
+ std::vector<JSAMPROW> cbLines(paddedHeight/cVSubSampling);
+ std::vector<JSAMPROW> crLines(paddedHeight/cVSubSampling);
+
+ uint8_t *py = static_cast<uint8_t*>(inLayout.y);
+ uint8_t *pcr = static_cast<uint8_t*>(inLayout.cr);
+ uint8_t *pcb = static_cast<uint8_t*>(inLayout.cb);
+
+ for(uint32_t i = 0; i < paddedHeight; i++)
+ {
+ /* Once we are in the padding territory we still point to the last line
+ * effectively replicating it several times ~ CLAMP_TO_EDGE */
+ int li = std::min(i, inSz.height - 1);
+ yLines[i] = static_cast<JSAMPROW>(py + li * inLayout.yStride);
+ if(i < paddedHeight / cVSubSampling)
+ {
+ crLines[i] = static_cast<JSAMPROW>(pcr + li * inLayout.cStride);
+ cbLines[i] = static_cast<JSAMPROW>(pcb + li * inLayout.cStride);
+ }
+ }
+
+ /* If APP1 data was passed in, use it */
+ if(app1Buffer && app1Size)
+ {
+ jpeg_write_marker(&cinfo, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(app1Buffer), app1Size);
+ }
+
+ /* While we still have padded height left to go, keep giving it one
+ * macroblock at a time. */
+ while (cinfo.next_scanline < cinfo.image_height) {
+ const uint32_t batchSize = DCTSIZE * maxVSampFactor;
+ const uint32_t nl = cinfo.next_scanline;
+ JSAMPARRAY planes[3]{ &yLines[nl],
+ &cbLines[nl/cVSubSampling],
+ &crLines[nl/cVSubSampling] };
+
+ uint32_t done = jpeg_write_raw_data(&cinfo, planes, batchSize);
+
+ if (done != batchSize) {
+ ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
+ __FUNCTION__, done, batchSize, cinfo.next_scanline,
+ cinfo.image_height);
+ return -1;
+ }
+ }
+
+ /* This will flush everything */
+ jpeg_finish_compress(&cinfo);
+
+ /* Grab the actual code size and set it */
+ actualCodeSize = dmgr.mEncodedSize;
+
+ return 0;
+}
+
+/*
+ * TODO: There needs to be a mechanism to discover allocated buffer size
+ * in the HAL.
+ *
+ * This is very fragile because it is duplicated computation from:
+ * frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp
+ *
+ */
+
+/* This assumes mSupportedFormats have all been declared as supporting
+ * HAL_PIXEL_FORMAT_BLOB to the framework */
+Size ExternalCameraDeviceSession::getMaxJpegResolution() const {
+ Size ret { 0, 0 };
+ for(auto & fmt : mSupportedFormats) {
+ if(fmt.width * fmt.height > ret.width * ret.height) {
+ ret = Size { fmt.width, fmt.height };
+ }
+ }
+ return ret;
+}
+
+Size ExternalCameraDeviceSession::getMaxThumbResolution() const {
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGW("%s: non-zero thumbnail size not available", __FUNCTION__);
+ }
+
+ return thumbSize;
+}
+
+
+ssize_t ExternalCameraDeviceSession::getJpegBufferSize(
+ uint32_t width, uint32_t height) const {
+ // Constant from camera3.h
+ const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(CameraBlob);
+ // Get max jpeg size (area-wise).
+ if (mMaxJpegResolution.width == 0) {
+ ALOGE("%s: Do not have a single supported JPEG stream",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // Get max jpeg buffer size
+ ssize_t maxJpegBufferSize = 0;
+ camera_metadata_ro_entry jpegBufMaxSize =
+ mCameraCharacteristics.find(ANDROID_JPEG_MAX_SIZE);
+ if (jpegBufMaxSize.count == 0) {
+ ALOGE("%s: Can't find maximum JPEG size in static metadata!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ maxJpegBufferSize = jpegBufMaxSize.data.i32[0];
+
+ if (maxJpegBufferSize <= kMinJpegBufferSize) {
+ ALOGE("%s: ANDROID_JPEG_MAX_SIZE (%zd) <= kMinJpegBufferSize (%zd)",
+ __FUNCTION__, maxJpegBufferSize, kMinJpegBufferSize);
+ return BAD_VALUE;
+ }
+
+ // Calculate final jpeg buffer size for the given resolution.
+ float scaleFactor = ((float) (width * height)) /
+ (mMaxJpegResolution.width * mMaxJpegResolution.height);
+ ssize_t jpegBufferSize = scaleFactor * (maxJpegBufferSize - kMinJpegBufferSize) +
+ kMinJpegBufferSize;
+ if (jpegBufferSize > maxJpegBufferSize) {
+ jpegBufferSize = maxJpegBufferSize;
+ }
+
+ return jpegBufferSize;
+}
+
+int ExternalCameraDeviceSession::OutputThread::createJpegLocked(
+ HalStreamBuffer &halBuf,
+ HalRequest &req)
+{
+ int ret;
+ auto lfail = [&](auto... args) {
+ ALOGE(args...);
+
+ return 1;
+ };
+ auto parent = mParent.promote();
+ if (parent == nullptr) {
+ ALOGE("%s: session has been disconnected!", __FUNCTION__);
+ return 1;
+ }
+
+ ALOGV("%s: HAL buffer sid: %d bid: %" PRIu64 " w: %u h: %u",
+ __FUNCTION__, halBuf.streamId, static_cast<uint64_t>(halBuf.bufferId),
+ halBuf.width, halBuf.height);
+ ALOGV("%s: HAL buffer fmt: %x usage: %" PRIx64 " ptr: %p",
+ __FUNCTION__, halBuf.format, static_cast<uint64_t>(halBuf.usage),
+ halBuf.bufPtr);
+ ALOGV("%s: YV12 buffer %d x %d",
+ __FUNCTION__,
+ mYu12Frame->mWidth, mYu12Frame->mHeight);
+
+ int jpegQuality, thumbQuality;
+ Size thumbSize;
+
+ if (req.setting.exists(ANDROID_JPEG_QUALITY)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_QUALITY);
+ jpegQuality = entry.data.u8[0];
+ } else {
+ return lfail("%s: ANDROID_JPEG_QUALITY not set",__FUNCTION__);
+ }
+
+ if (req.setting.exists(ANDROID_JPEG_THUMBNAIL_QUALITY)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_THUMBNAIL_QUALITY);
+ thumbQuality = entry.data.u8[0];
+ } else {
+ return lfail(
+ "%s: ANDROID_JPEG_THUMBNAIL_QUALITY not set",
+ __FUNCTION__);
+ }
+
+ if (req.setting.exists(ANDROID_JPEG_THUMBNAIL_SIZE)) {
+ camera_metadata_entry entry =
+ req.setting.find(ANDROID_JPEG_THUMBNAIL_SIZE);
+ thumbSize = Size { static_cast<uint32_t>(entry.data.i32[0]),
+ static_cast<uint32_t>(entry.data.i32[1])
+ };
+ } else {
+ return lfail(
+ "%s: ANDROID_JPEG_THUMBNAIL_SIZE not set", __FUNCTION__);
+ }
+
+ /* Cropped and scaled YU12 buffer for main and thumbnail */
+ YCbCrLayout yu12Main;
+ Size jpegSize { halBuf.width, halBuf.height };
+
+ /* Compute temporary buffer sizes accounting for the following:
+ * thumbnail can't exceed APP1 size of 64K
+ * main image needs to hold APP1, headers, and at most a poorly
+ * compressed image */
+ const ssize_t maxThumbCodeSize = 64 * 1024;
+ const ssize_t maxJpegCodeSize = parent->getJpegBufferSize(jpegSize.width,
+ jpegSize.height);
+
+ /* Check that getJpegBufferSize did not return an error */
+ if (maxJpegCodeSize < 0) {
+ return lfail(
+ "%s: getJpegBufferSize returned %zd",__FUNCTION__,maxJpegCodeSize);
+ }
+
+
+ /* Hold actual thumbnail and main image code sizes */
+ size_t thumbCodeSize = 0, jpegCodeSize = 0;
+ /* Temporary thumbnail code buffer */
+ std::vector<uint8_t> thumbCode(maxThumbCodeSize);
+
+ YCbCrLayout yu12Thumb;
+ ret = cropAndScaleThumbLocked(mYu12Frame, thumbSize, &yu12Thumb);
+
+ if (ret != 0) {
+ return lfail(
+ "%s: crop and scale thumbnail failed!", __FUNCTION__);
+ }
+
+ /* Scale and crop main jpeg */
+ ret = cropAndScaleLocked(mYu12Frame, jpegSize, &yu12Main);
+
+ if (ret != 0) {
+ return lfail("%s: crop and scale main failed!", __FUNCTION__);
+ }
+
+ /* Encode the thumbnail image */
+ ret = encodeJpegYU12(thumbSize, yu12Thumb,
+ thumbQuality, 0, 0,
+ &thumbCode[0], maxThumbCodeSize, thumbCodeSize);
+
+ if (ret != 0) {
+ return lfail("%s: encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+ }
+
+ /* Combine camera characteristics with request settings to form EXIF
+ * metadata */
+ common::V1_0::helper::CameraMetadata meta(parent->mCameraCharacteristics);
+ meta.append(req.setting);
+
+ /* Generate EXIF object */
+ std::unique_ptr<ExifUtils> utils(ExifUtils::create());
+ /* Make sure it's initialized */
+ utils->initialize();
+
+ utils->setFromMetadata(meta, jpegSize.width, jpegSize.height);
+
+ /* Check if we made a non-zero-sized thumbnail. Currently not possible
+ * that we got this far and the code is size 0, but if this code moves
+ * around it might become relevant again */
+
+ ret = utils->generateApp1(thumbCodeSize ? &thumbCode[0] : 0, thumbCodeSize);
+
+ if (!ret) {
+ return lfail("%s: generating APP1 failed", __FUNCTION__);
+ }
+
+ /* Get internal buffer */
+ size_t exifDataSize = utils->getApp1Length();
+ const uint8_t* exifData = utils->getApp1Buffer();
+
+ /* Lock the HAL jpeg code buffer */
+ void *bufPtr = sHandleImporter.lock(
+ *(halBuf.bufPtr), halBuf.usage, maxJpegCodeSize);
+
+ if (!bufPtr) {
+ return lfail("%s: could not lock %zu bytes", __FUNCTION__, maxJpegCodeSize);
+ }
+
+ /* Encode the main jpeg image */
+ ret = encodeJpegYU12(jpegSize, yu12Main,
+ jpegQuality, exifData, exifDataSize,
+ bufPtr, maxJpegCodeSize, jpegCodeSize);
+
+ /* TODO: Not sure this belongs here, maybe better to pass jpegCodeSize out
+ * and do this when returning buffer to parent */
+ CameraBlob blob { CameraBlobId::JPEG, static_cast<uint32_t>(jpegCodeSize) };
+ void *blobDst =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(bufPtr) +
+ maxJpegCodeSize -
+ sizeof(CameraBlob));
+ memcpy(blobDst, &blob, sizeof(CameraBlob));
+
+ /* Unlock the HAL jpeg code buffer */
+ int relFence = sHandleImporter.unlock(*(halBuf.bufPtr));
+ if (relFence > 0) {
+ halBuf.acquireFence = relFence;
+ }
+
+ /* Check if our JPEG actually succeeded */
+ if (ret != 0) {
+ return lfail(
+ "%s: encodeJpegYU12 failed with %d",__FUNCTION__, ret);
+ }
+
+ ALOGV("%s: encoded JPEG (ret:%d) with Q:%d max size: %zu",
+ __FUNCTION__, ret, jpegQuality, maxJpegCodeSize);
+
+ return 0;
+}
+
bool ExternalCameraDeviceSession::OutputThread::threadLoop() {
HalRequest req;
auto parent = mParent.promote();
@@ -997,9 +1612,21 @@
// Gralloc lockYCbCr the buffer
switch (halBuf.format) {
- case PixelFormat::BLOB:
- // TODO: b/72261675 implement JPEG output path
- break;
+ case PixelFormat::BLOB: {
+ int ret = createJpegLocked(halBuf, req);
+
+ if(ret != 0) {
+ ALOGE("%s: createJpegLocked failed with %d",
+ __FUNCTION__, ret);
+ lk.unlock();
+ parent->notifyError(
+ /*frameNum*/req.frameNumber,
+ /*stream*/-1,
+ ErrorCode::ERROR_DEVICE);
+
+ return false;
+ }
+ } break;
case PixelFormat::YCBCR_420_888:
case PixelFormat::YV12: {
IMapper::Rect outRect {0, 0,
@@ -1021,7 +1648,9 @@
YCbCrLayout cropAndScaled;
int ret = cropAndScaleLocked(
- mYu12Frame, halBuf, &cropAndScaled);
+ mYu12Frame,
+ Size { halBuf.width, halBuf.height },
+ &cropAndScaled);
if (ret != 0) {
ALOGE("%s: crop and scale failed!", __FUNCTION__);
lk.unlock();
@@ -1067,7 +1696,8 @@
}
Status ExternalCameraDeviceSession::OutputThread::allocateIntermediateBuffers(
- const Size& v4lSize, const hidl_vec<Stream>& streams) {
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams) {
std::lock_guard<std::mutex> lk(mLock);
if (mScaledYu12Frames.size() != 0) {
ALOGE("%s: intermediate buffer pool has %zu inflight buffers! (expect 0)",
@@ -1087,6 +1717,19 @@
}
}
+ // Allocating intermediate YU12 thumbnail frame
+ if (mYu12ThumbFrame == nullptr ||
+ mYu12ThumbFrame->mWidth != thumbSize.width ||
+ mYu12ThumbFrame->mHeight != thumbSize.height) {
+ mYu12ThumbFrame.clear();
+ mYu12ThumbFrame = new AllocatedFrame(thumbSize.width, thumbSize.height);
+ int ret = mYu12ThumbFrame->allocate(&mYu12ThumbFrameLayout);
+ if (ret != 0) {
+ ALOGE("%s: allocating YU12 thumb frame failed!", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+ }
+
// Allocating scaled buffers
for (const auto& stream : streams) {
Size sz = {stream.width, stream.height};
@@ -1626,7 +2269,24 @@
}
Size v4lSize = {v4l2Fmt.width, v4l2Fmt.height};
- status = mOutputThread->allocateIntermediateBuffers(v4lSize, config.streams);
+ Size thumbSize { 0, 0 };
+ camera_metadata_ro_entry entry =
+ mCameraCharacteristics.find(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES);
+ for(uint32_t i = 0; i < entry.count; i += 2) {
+ Size sz { static_cast<uint32_t>(entry.data.i32[i]),
+ static_cast<uint32_t>(entry.data.i32[i+1]) };
+ if(sz.width * sz.height > thumbSize.width * thumbSize.height) {
+ thumbSize = sz;
+ }
+ }
+
+ if (thumbSize.width * thumbSize.height == 0) {
+ ALOGE("%s: non-zero thumbnail size not available", __FUNCTION__);
+ return Status::INTERNAL_ERROR;
+ }
+
+ status = mOutputThread->allocateIntermediateBuffers(v4lSize,
+ mMaxThumbResolution, config.streams);
if (status != Status::OK) {
ALOGE("%s: allocating intermediate buffers failed!", __FUNCTION__);
return status;
@@ -1647,6 +2307,7 @@
switch (config.streams[i].format) {
case PixelFormat::BLOB:
case PixelFormat::YCBCR_420_888:
+ case PixelFormat::YV12: // Used by SurfaceTexture
// No override
out->streams[i].v3_2.overrideFormat = config.streams[i].format;
break;
@@ -1659,7 +2320,7 @@
mStreamMap[config.streams[i].id].format = out->streams[i].v3_2.overrideFormat;
break;
default:
- ALOGE("%s: unsupported format %x", __FUNCTION__, config.streams[i].format);
+ ALOGE("%s: unsupported format 0x%x", __FUNCTION__, config.streams[i].format);
return Status::ILLEGAL_ARGUMENT;
}
}
@@ -1767,26 +2428,26 @@
const uint8_t controlMode = ANDROID_CONTROL_MODE_AUTO;
UPDATE(md, ANDROID_CONTROL_MODE, &controlMode, 1);
- for (int type = static_cast<int>(RequestTemplate::PREVIEW);
- type <= static_cast<int>(RequestTemplate::VIDEO_SNAPSHOT); type++) {
+ auto requestTemplates = hidl_enum_iterator<RequestTemplate>();
+ for (RequestTemplate type : requestTemplates) {
::android::hardware::camera::common::V1_0::helper::CameraMetadata mdCopy = md;
uint8_t intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
switch (type) {
- case static_cast<int>(RequestTemplate::PREVIEW):
+ case RequestTemplate::PREVIEW:
intent = ANDROID_CONTROL_CAPTURE_INTENT_PREVIEW;
break;
- case static_cast<int>(RequestTemplate::STILL_CAPTURE):
+ case RequestTemplate::STILL_CAPTURE:
intent = ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE;
break;
- case static_cast<int>(RequestTemplate::VIDEO_RECORD):
+ case RequestTemplate::VIDEO_RECORD:
intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_RECORD;
break;
- case static_cast<int>(RequestTemplate::VIDEO_SNAPSHOT):
+ case RequestTemplate::VIDEO_SNAPSHOT:
intent = ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT;
break;
default:
- ALOGE("%s: unknown template type %d", __FUNCTION__, type);
- return BAD_VALUE;
+ ALOGV("%s: unsupported RequestTemplate type %d", __FUNCTION__, type);
+ continue;
}
UPDATE(mdCopy, ANDROID_CONTROL_CAPTURE_INTENT, &intent, 1);
@@ -1813,15 +2474,14 @@
const uint8_t ae_lock = ANDROID_CONTROL_AE_LOCK_OFF;
UPDATE(md, ANDROID_CONTROL_AE_LOCK, &ae_lock, 1);
-
- // TODO: b/72261912 AF should stay LOCKED until cancel is seen
- bool afTrigger = false;
+ bool afTrigger = mAfTrigger;
if (md.exists(ANDROID_CONTROL_AF_TRIGGER)) {
+ Mutex::Autolock _l(mLock);
camera_metadata_entry entry = md.find(ANDROID_CONTROL_AF_TRIGGER);
if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_START) {
- afTrigger = true;
+ mAfTrigger = afTrigger = true;
} else if (entry.data.u8[0] == ANDROID_CONTROL_AF_TRIGGER_CANCEL) {
- afTrigger = false;
+ mAfTrigger = afTrigger = false;
}
}
@@ -1851,6 +2511,9 @@
return -EINVAL;
}
+ const uint8_t flashState = ANDROID_FLASH_STATE_UNAVAILABLE;
+ UPDATE(md, ANDROID_FLASH_STATE, &flashState, 1);
+
// android.scaler
const int32_t crop_region[] = {
active_array_size.data.i32[0], active_array_size.data.i32[1],
@@ -1987,4 +2650,3 @@
} // namespace camera
} // namespace hardware
} // namespace android
-
diff --git a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
index fbde083..9cd7da7 100644
--- a/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
+++ b/camera/device/3.4/default/include/device_v3_4_impl/CameraDeviceSession.h
@@ -19,6 +19,7 @@
#include <android/hardware/camera/device/3.2/ICameraDevice.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
+#include <android/hardware/camera/device/3.4/ICameraDeviceCallback.h>
#include <../../3.3/default/CameraDeviceSession.h>
#include <../../3.3/default/include/convert.h>
#include <fmq/MessageQueue.h>
@@ -46,6 +47,7 @@
using ::android::hardware::camera::device::V3_4::StreamConfiguration;
using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
+using ::android::hardware::camera::device::V3_4::ICameraDeviceCallback;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
using ::android::hardware::kSynchronizedReadWrite;
@@ -73,6 +75,9 @@
// Methods from v3.3 and earlier will trampoline to inherited implementation
// New methods for v3.4
+ Return<void> constructDefaultRequestSettings_3_4(
+ RequestTemplate type,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
Return<void> configureStreams_3_4(
const StreamConfiguration& requestedConfiguration,
@@ -91,6 +96,25 @@
Status processOneCaptureRequest_3_4(const V3_4::CaptureRequest& request);
std::map<int, std::string> mPhysicalCameraIdMap;
+
+ static V3_2::implementation::callbacks_process_capture_result_t sProcessCaptureResult_3_4;
+ static V3_2::implementation::callbacks_notify_t sNotify_3_4;
+
+ class ResultBatcher_3_4 : public V3_3::implementation::CameraDeviceSession::ResultBatcher {
+ public:
+ ResultBatcher_3_4(const sp<V3_2::ICameraDeviceCallback>& callback);
+ void processCaptureResult_3_4(CaptureResult& result);
+ private:
+ void freeReleaseFences_3_4(hidl_vec<CaptureResult>&);
+ void processOneCaptureResult_3_4(CaptureResult& result);
+ void invokeProcessCaptureResultCallback_3_4(hidl_vec<CaptureResult> &results,
+ bool tryWriteFmq);
+
+ sp<ICameraDeviceCallback> mCallback_3_4;
+ } mResultBatcher_3_4;
+
+ // Whether this camera device session is created with version 3.4 callback.
+ bool mHasCallback_3_4;
private:
struct TrampolineSessionInterface_3_4 : public ICameraDeviceSession {
@@ -139,6 +163,12 @@
return mParent->close();
}
+ virtual Return<void> constructDefaultRequestSettings_3_4(
+ RequestTemplate type,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override {
+ return mParent->constructDefaultRequestSettings_3_4(type, _hidl_cb);
+ }
+
virtual Return<void> configureStreams_3_3(
const V3_2::StreamConfiguration& requestedConfiguration,
configureStreams_3_3_cb _hidl_cb) override {
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 404dfe0..5856306 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -30,6 +30,7 @@
#include <unordered_set>
#include "CameraMetadata.h"
#include "HandleImporter.h"
+#include "Exif.h"
#include "utils/KeyedVector.h"
#include "utils/Mutex.h"
#include "utils/Thread.h"
@@ -51,17 +52,20 @@
using ::android::hardware::camera::device::V3_2::ICameraDeviceCallback;
using ::android::hardware::camera::device::V3_2::MsgType;
using ::android::hardware::camera::device::V3_2::NotifyMsg;
-using ::android::hardware::camera::device::V3_2::RequestTemplate;
+using ::android::hardware::camera::device::V3_4::RequestTemplate;
using ::android::hardware::camera::device::V3_2::Stream;
using ::android::hardware::camera::device::V3_4::StreamConfiguration;
using ::android::hardware::camera::device::V3_2::StreamConfigurationMode;
using ::android::hardware::camera::device::V3_2::StreamRotation;
using ::android::hardware::camera::device::V3_2::StreamType;
using ::android::hardware::camera::device::V3_2::DataspaceFlags;
+using ::android::hardware::camera::device::V3_2::CameraBlob;
+using ::android::hardware::camera::device::V3_2::CameraBlobId;
using ::android::hardware::camera::device::V3_4::HalStreamConfiguration;
using ::android::hardware::camera::device::V3_4::ICameraDeviceSession;
using ::android::hardware::camera::common::V1_0::Status;
using ::android::hardware::camera::common::V1_0::helper::HandleImporter;
+using ::android::hardware::camera::common::V1_0::helper::ExifUtils;
using ::android::hardware::graphics::common::V1_0::BufferUsage;
using ::android::hardware::graphics::common::V1_0::Dataspace;
using ::android::hardware::graphics::common::V1_0::PixelFormat;
@@ -172,7 +176,11 @@
// Methods from ::android::hardware::camera::device::V3_2::ICameraDeviceSession follow
Return<void> constructDefaultRequestSettings(
- RequestTemplate,
+ V3_2::RequestTemplate,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
+
+ Return<void> constructDefaultRequestSettings_3_4(
+ RequestTemplate type,
ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb);
Return<void> configureStreams(
@@ -227,6 +235,9 @@
std::vector<HalStreamBuffer> buffers;
};
+ Status constructDefaultRequestSettingsRaw(RequestTemplate type,
+ V3_2::CameraMetadata *outMetadata);
+
static std::vector<SupportedV4L2Format> sortFormats(
const std::vector<SupportedV4L2Format>&);
static CroppingType initCroppingType(const std::vector<SupportedV4L2Format>&);
@@ -265,13 +276,19 @@
hidl_vec<CaptureResult> &results, bool tryWriteFmq);
static void freeReleaseFences(hidl_vec<CaptureResult>&);
+ Size getMaxJpegResolution() const;
+ Size getMaxThumbResolution() const;
+
+ ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;
+
class OutputThread : public android::Thread {
public:
OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
~OutputThread();
Status allocateIntermediateBuffers(
- const Size& v4lSize, const hidl_vec<Stream>& streams);
+ const Size& v4lSize, const Size& thumbSize,
+ const hidl_vec<Stream>& streams);
Status submitRequest(const HalRequest&);
void flush();
virtual bool threadLoop() override;
@@ -289,12 +306,24 @@
void waitForNextRequest(HalRequest* out);
int cropAndScaleLocked(
- sp<AllocatedFrame>& in, const HalStreamBuffer& halBuf,
+ sp<AllocatedFrame>& in, const Size& outSize,
+ YCbCrLayout* out);
+
+ int cropAndScaleThumbLocked(
+ sp<AllocatedFrame>& in, const Size& outSize,
YCbCrLayout* out);
int formatConvertLocked(const YCbCrLayout& in, const YCbCrLayout& out,
Size sz, uint32_t format);
+ static int encodeJpegYU12(const Size &inSz,
+ const YCbCrLayout& inLayout, int jpegQuality,
+ const void *app1Buffer, size_t app1Size,
+ void *out, size_t maxOutSize,
+ size_t &actualCodeSize);
+
+ int createJpegLocked(HalStreamBuffer &halBuf, HalRequest &req);
+
mutable std::mutex mLock;
std::condition_variable mRequestCond;
wp<ExternalCameraDeviceSession> mParent;
@@ -305,9 +334,11 @@
// (Scale)-> mScaledYu12Frames
// (Format convert) -> output gralloc frames
sp<AllocatedFrame> mYu12Frame;
+ sp<AllocatedFrame> mYu12ThumbFrame;
std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mIntermediateBuffers;
std::unordered_map<Size, sp<AllocatedFrame>, SizeHasher> mScaledYu12Frames;
YCbCrLayout mYu12FrameLayout;
+ YCbCrLayout mYu12ThumbFrameLayout;
};
// Protect (most of) HIDL interface methods from synchronized-entering
@@ -352,6 +383,8 @@
// Stream ID -> circulating buffers map
std::map<int, CirculatingBuffers> mCirculatingBuffers;
+ bool mAfTrigger = false;
+
static HandleImporter sHandleImporter;
/* Beginning of members not changed after initialize() */
@@ -363,7 +396,10 @@
// Protect against invokeProcessCaptureResultCallback()
Mutex mProcessCaptureResultLock;
- std::unordered_map<int, CameraMetadata> mDefaultRequests;
+ std::unordered_map<RequestTemplate, CameraMetadata> mDefaultRequests;
+
+ const Size mMaxThumbResolution;
+ const Size mMaxJpegResolution;
/* End of members not changed after initialize() */
private:
@@ -408,6 +444,12 @@
return mParent->close();
}
+ virtual Return<void> constructDefaultRequestSettings_3_4(
+ RequestTemplate type,
+ ICameraDeviceSession::constructDefaultRequestSettings_cb _hidl_cb) override {
+ return mParent->constructDefaultRequestSettings_3_4(type, _hidl_cb);
+ }
+
virtual Return<void> configureStreams_3_3(
const V3_2::StreamConfiguration& requestedConfiguration,
configureStreams_3_3_cb _hidl_cb) override {
diff --git a/camera/device/3.4/types.hal b/camera/device/3.4/types.hal
index 75c64ab..d80ab67 100644
--- a/camera/device/3.4/types.hal
+++ b/camera/device/3.4/types.hal
@@ -16,12 +16,13 @@
package android.hardware.camera.device@3.4;
-import @3.2::types;
+import @3.2::RequestTemplate;
import @3.2::StreamConfigurationMode;
import @3.2::Stream;
import @3.3::HalStream;
import @3.2::CameraMetadata;
import @3.2::CaptureRequest;
+import @3.2::CaptureResult;
/**
* Stream:
@@ -77,6 +78,36 @@
};
/**
+ * New request templates, extending the @3.2 RequestTemplate
+ */
+enum RequestTemplate : @3.2::RequestTemplate {
+ /**
+ * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
+ * possible while improving the camera output for motion tracking use cases.
+ *
+ * This template is best used by applications that are frequently switching between motion
+ * tracking use cases and regular still capture use cases, to minimize the IQ changes
+ * when swapping use cases.
+ *
+ * This template is guaranteed to be supported on camera devices that support the
+ * REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING capability.
+ */
+ MOTION_TRACKING_PREVIEW = 7,
+
+ /**
+ * A template for selecting camera parameters that maximize the quality of camera output for
+ * motion tracking use cases.
+ *
+ * This template is best used by applications dedicated to motion tracking applications,
+ * which aren't concerned about fast switches between motion tracking and other use cases.
+ *
+ * This template is guaranteed to be supported on camera devices that support the
+ * REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING capability.
+ */
+ MOTION_TRACKING_BEST = 8,
+};
+
+/**
* StreamConfiguration:
*
* Identical to @3.2::StreamConfiguration, except that it contains session
@@ -211,3 +242,66 @@
*/
vec<PhysicalCameraSetting> physicalCameraSettings;
};
+
+/**
+ * PhysicalCameraMetadata:
+ *
+ * Individual camera metadata for a physical camera as part of a logical
+ * multi-camera. Camera HAL should return one such metadata for each physical
+ * camera being requested on.
+ */
+struct PhysicalCameraMetadata {
+ /**
+ * If non-zero, read metadata from result metadata queue instead
+ * (see ICameraDeviceSession.getCaptureResultMetadataQueue).
+ * If zero, read metadata from .metadata field.
+ */
+ uint64_t fmqMetadataSize;
+
+ /**
+ * Contains the physical device camera id. As long as the corresponding
+ * processCaptureRequest requests on a particular physical camera stream,
+ * the metadata for that physical camera should be generated for the capture
+ * result. */
+ string physicalCameraId;
+
+ /**
+ * If fmqMetadataSize is zero, the metadata buffer contains the metadata
+ * for the physical device with physicalCameraId.
+ *
+ * The v3_2 CaptureResult metadata is read first from the FMQ, followed by
+ * the physical cameras' metadata starting from index 0.
+ */
+ CameraMetadata metadata;
+};
+
+/**
+ * CaptureResult:
+ *
+ * Identical to @3.2::CaptureResult, except that it contains a list of
+ * physical camera metadata.
+ *
+ * Physical camera metadata needs to be generated if and only if a
+ * request is pending on a stream from that physical camera. For example,
+ * if the processCaptureRequest call doesn't request on physical camera
+ * streams, the physicalCameraMetadata field of the CaptureResult being returned
+ * should be an 0-size vector. If the processCaptureRequest call requests on
+ * streams from one of the physical camera, the physicalCameraMetadata field
+ * should contain one metadata describing the capture from that physical camera.
+ *
+ * For a CaptureResult that contains physical camera metadata, its
+ * partialResult field must be android.request.partialResultCount. In other
+ * words, the physicalCameraMetadata must only be contained in a final capture
+ * result.
+ */
+struct CaptureResult {
+ /**
+ * The definition of CaptureResult from the prior version.
+ */
+ @3.2::CaptureResult v3_2;
+
+ /**
+ * The physical metadata for logical multi-camera.
+ */
+ vec<PhysicalCameraMetadata> physicalCameraMetadata;
+};
diff --git a/camera/metadata/3.3/Android.bp b/camera/metadata/3.3/Android.bp
index 23c1036..166c2ac 100644
--- a/camera/metadata/3.3/Android.bp
+++ b/camera/metadata/3.3/Android.bp
@@ -13,11 +13,14 @@
"android.hardware.camera.metadata@3.2",
],
types: [
+ "CameraMetadataEnumAndroidControlAeMode",
"CameraMetadataEnumAndroidControlAfSceneChange",
"CameraMetadataEnumAndroidControlCaptureIntent",
+ "CameraMetadataEnumAndroidInfoSupportedHardwareLevel",
"CameraMetadataEnumAndroidLensPoseReference",
- "CameraMetadataEnumAndroidRequestAvailableCapabilities",
"CameraMetadataEnumAndroidLogicalMultiCameraSensorSyncType",
+ "CameraMetadataEnumAndroidRequestAvailableCapabilities",
+ "CameraMetadataEnumAndroidStatisticsOisDataMode",
"CameraMetadataSection",
"CameraMetadataSectionStart",
"CameraMetadataTag",
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index 2a01480..1c86f11 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -16,123 +16,78 @@
LOCAL_PATH := $(call my-dir)
+BUILD_FRAMEWORK_COMPATIBILITY_MATRIX := $(LOCAL_PATH)/compatibility_matrix.mk
+
+# Clear potential input variables to BUILD_FRAMEWORK_COMPATIBILITY_MATRIX
+LOCAL_ADD_VBMETA_VERSION :=
+LOCAL_ASSEMBLE_VINTF_ENV_VARS :=
+LOCAL_ASSEMBLE_VINTF_FLAGS :=
+LOCAL_KERNEL_VERSIONS :=
+LOCAL_GEN_FILE_DEPENDENCIES :=
+
# Install all compatibility_matrix.*.xml to /system/etc/vintf
+
include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.legacy.xml
LOCAL_MODULE_STEM := compatibility_matrix.legacy.xml
LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
-include $(BUILD_PREBUILT)
+LOCAL_KERNEL_VERSIONS := 3.18.0 4.4.0 4.9.0
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.1.xml
LOCAL_MODULE_STEM := compatibility_matrix.1.xml
LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
-include $(BUILD_PREBUILT)
+LOCAL_KERNEL_VERSIONS := 3.18.0 4.4.0 4.9.0
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.2.xml
LOCAL_MODULE_STEM := compatibility_matrix.2.xml
LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
-include $(BUILD_PREBUILT)
+LOCAL_KERNEL_VERSIONS := 3.18.0 4.4.0 4.9.0
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
+
+# TODO(b/72409164): STOPSHIP: update kernel version requirements
include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.current.xml
LOCAL_MODULE_STEM := compatibility_matrix.current.xml
LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
-include $(BUILD_PREBUILT)
+LOCAL_KERNEL_VERSIONS := 4.4.0 4.9.0
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
-# Framework Compatibility Matrix without HALs
+# Framework Compatibility Matrix (common to all FCM versions)
+
include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.empty.xml
-LOCAL_MODULE_STEM := compatibility_matrix.empty.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
+LOCAL_MODULE_STEM := compatibility_matrix.empty.xml
+LOCAL_SRC_FILES := $(LOCAL_MODULE_STEM)
+LOCAL_ADD_VBMETA_VERSION := true
+LOCAL_ASSEMBLE_VINTF_ENV_VARS := \
+ POLICYVERS \
+ BOARD_SEPOLICY_VERS
-GEN := $(local-generated-sources-dir)/$(LOCAL_MODULE_STEM)
-
-$(GEN): PRIVATE_FLAGS :=
-
-ifeq (true,$(BOARD_AVB_ENABLE))
-$(GEN): $(AVBTOOL)
-# INTERNAL_AVB_SYSTEM_SIGNING_ARGS consists of BOARD_AVB_SYSTEM_KEY_PATH and
-# BOARD_AVB_SYSTEM_ALGORITHM. We should add the dependency of key path, which
-# is a file, here.
-$(GEN): $(BOARD_AVB_SYSTEM_KEY_PATH)
-# Use deferred assignment (=) instead of immediate assignment (:=).
-# Otherwise, cannot get INTERNAL_AVB_SYSTEM_SIGNING_ARGS.
-$(GEN): FRAMEWORK_VBMETA_VERSION = $$("$(AVBTOOL)" add_hashtree_footer \
- --print_required_libavb_version \
- $(INTERNAL_AVB_SYSTEM_SIGNING_ARGS) \
- $(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS))
-else
-$(GEN): FRAMEWORK_VBMETA_VERSION := 0.0
-endif
-
-# Specify kernel versions that the current framework supports. These versions,
-# along with kernel configurations, are written to the framework compatibility
-# matrix.
-$(GEN): KERNEL_VERSIONS := 3.18 4.4 4.9
-
-# Specify the location of android-base*.cfg files.
-$(GEN): KERNEL_CONFIG_DATA := kernel/configs
-
-$(GEN): $(foreach version,$(KERNEL_VERSIONS),\
- $(wildcard $(KERNEL_CONFIG_DATA)/android-$(version)/android-base*.cfg))
-$(GEN): PRIVATE_FLAGS += $(foreach version,$(KERNEL_VERSIONS),\
- --kernel=$(version):$(call normalize-path-list,\
- $(wildcard $(KERNEL_CONFIG_DATA)/android-$(version)/android-base*.cfg)))
-
-$(GEN): $(LOCAL_PATH)/compatibility_matrix.empty.xml $(HOST_OUT_EXECUTABLES)/assemble_vintf
- POLICYVERS=$(POLICYVERS) \
- BOARD_SEPOLICY_VERS=$(BOARD_SEPOLICY_VERS) \
- FRAMEWORK_VBMETA_VERSION=$(FRAMEWORK_VBMETA_VERSION) \
- $(HOST_OUT_EXECUTABLES)/assemble_vintf \
- -i $< -o $@ $(PRIVATE_FLAGS)
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
# Framework Compatibility Matrix
-include $(CLEAR_VARS)
-LOCAL_MODULE := framework_compatibility_matrix.xml
-LOCAL_MODULE_STEM := compatibility_matrix.xml
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)
+include $(CLEAR_VARS)
+LOCAL_MODULE := framework_compatibility_matrix.xml
+LOCAL_MODULE_STEM := compatibility_matrix.xml
+LOCAL_MODULE_PATH := $(TARGET_OUT)
LOCAL_REQUIRED_MODULES := \
framework_compatibility_matrix.legacy.xml \
framework_compatibility_matrix.1.xml \
framework_compatibility_matrix.2.xml \
framework_compatibility_matrix.current.xml \
framework_compatibility_matrix.empty.xml
-
-GEN := $(local-generated-sources-dir)/compatibility_matrix.xml
-
-$(GEN): PRIVATE_FLAGS :=
+LOCAL_GENERATED_SOURCES := $(call module-installed-files,$(LOCAL_REQUIRED_MODULES))
ifdef BUILT_VENDOR_MANIFEST
-$(GEN): $(BUILT_VENDOR_MANIFEST)
-$(GEN): PRIVATE_FLAGS += -c "$(BUILT_VENDOR_MANIFEST)"
+LOCAL_GEN_FILE_DEPENDENCIES += $(BUILT_VENDOR_MANIFEST)
+LOCAL_ASSEMBLE_VINTF_FLAGS += -c "$(BUILT_VENDOR_MANIFEST)"
endif
-MATRIX_SRC_FILES := $(call module-installed-files,$(LOCAL_REQUIRED_MODULES))
-$(GEN): PRIVATE_MATRIX_SRC_FILES := $(MATRIX_SRC_FILES)
-$(GEN): $(MATRIX_SRC_FILES) $(HOST_OUT_EXECUTABLES)/assemble_vintf
- PRODUCT_ENFORCE_VINTF_MANIFEST=$(PRODUCT_ENFORCE_VINTF_MANIFEST) \
- $(HOST_OUT_EXECUTABLES)/assemble_vintf \
- -i $(call normalize-path-list,$(PRIVATE_MATRIX_SRC_FILES)) \
- -o $@ $(PRIVATE_FLAGS)
+LOCAL_ASSEMBLE_VINTF_ENV_VARS := PRODUCT_ENFORCE_VINTF_MANIFEST
-MATRIX_SRC_FILES :=
-
-LOCAL_PREBUILT_MODULE_FILE := $(GEN)
-include $(BUILD_PREBUILT)
+include $(BUILD_FRAMEWORK_COMPATIBILITY_MATRIX)
BUILT_SYSTEM_COMPATIBILITY_MATRIX := $(LOCAL_BUILT_MODULE)
+
+BUILD_FRAMEWORK_COMPATIBILITY_MATRIX :=
diff --git a/compatibility_matrices/compatibility_matrix.mk b/compatibility_matrices/compatibility_matrix.mk
new file mode 100644
index 0000000..14c60ab
--- /dev/null
+++ b/compatibility_matrices/compatibility_matrix.mk
@@ -0,0 +1,100 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+###########################################################
+## Remove minor revision from a kernel version. For example,
+## 3.18.0 becomes 3.18.
+## $(1): kernel version
+###########################################################
+define remove-minor-revision
+$(strip $(subst $(space),.,$(wordlist 1,2,$(subst .,$(space),$(strip $(1))))))
+endef
+
+# $(warning $(call remove-minor-revision,3.18.0))
+
+ifndef LOCAL_MODULE_STEM
+$(error LOCAL_MODULE_STEM must be defined.)
+endif
+
+LOCAL_MODULE := framework_$(LOCAL_MODULE_STEM)
+LOCAL_MODULE_CLASS := ETC
+
+ifndef LOCAL_MODULE_PATH
+LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/vintf
+endif
+
+GEN := $(local-generated-sources-dir)/$(LOCAL_MODULE_STEM)
+
+$(GEN): PRIVATE_ENV_VARS := $(LOCAL_ASSEMBLE_VINTF_ENV_VARS)
+$(GEN): PRIVATE_FLAGS := $(LOCAL_ASSEMBLE_VINTF_FLAGS)
+
+$(GEN): $(LOCAL_GEN_FILE_DEPENDENCIES)
+
+ifeq (true,$(strip $(LOCAL_ADD_VBMETA_VERSION)))
+ifeq (true,$(BOARD_AVB_ENABLE))
+$(GEN): $(AVBTOOL)
+# INTERNAL_AVB_SYSTEM_SIGNING_ARGS consists of BOARD_AVB_SYSTEM_KEY_PATH and
+# BOARD_AVB_SYSTEM_ALGORITHM. We should add the dependency of key path, which
+# is a file, here.
+$(GEN): $(BOARD_AVB_SYSTEM_KEY_PATH)
+# Use deferred assignment (=) instead of immediate assignment (:=).
+# Otherwise, cannot get INTERNAL_AVB_SYSTEM_SIGNING_ARGS.
+$(GEN): FRAMEWORK_VBMETA_VERSION = $$("$(AVBTOOL)" add_hashtree_footer \
+ --print_required_libavb_version \
+ $(INTERNAL_AVB_SYSTEM_SIGNING_ARGS) \
+ $(BOARD_AVB_SYSTEM_ADD_HASHTREE_FOOTER_ARGS))
+else
+$(GEN): FRAMEWORK_VBMETA_VERSION := 0.0
+endif # BOARD_AVB_ENABLE
+$(GEN): PRIVATE_ENV_VARS += FRAMEWORK_VBMETA_VERSION
+endif # LOCAL_ADD_VBMETA_VERSION
+
+ifneq (,$(strip $(LOCAL_KERNEL_VERSIONS)))
+$(GEN): PRIVATE_KERNEL_CONFIG_DATA := kernel/configs
+$(GEN): PRIVATE_KERNEL_VERSIONS := $(LOCAL_KERNEL_VERSIONS)
+$(GEN): $(foreach version,$(PRIVATE_KERNEL_VERSIONS),\
+ $(wildcard $(PRIVATE_KERNEL_CONFIG_DATA)/android-$(call remove-minor-revision,$(version))/android-base*.cfg))
+$(GEN): PRIVATE_FLAGS += $(foreach version,$(PRIVATE_KERNEL_VERSIONS),\
+ --kernel=$(version):$(call normalize-path-list,\
+ $(wildcard $(PRIVATE_KERNEL_CONFIG_DATA)/android-$(call remove-minor-revision,$(version))/android-base*.cfg)))
+endif
+
+my_matrix_src_files := \
+ $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES)) \
+ $(LOCAL_GENERATED_SOURCES)
+
+$(GEN): PRIVATE_SRC_FILES := $(my_matrix_src_files)
+$(GEN): $(my_matrix_src_files) $(HOST_OUT_EXECUTABLES)/assemble_vintf
+ $(foreach varname,$(PRIVATE_ENV_VARS),$(varname)=$($(varname))) \
+ $(HOST_OUT_EXECUTABLES)/assemble_vintf \
+ -i $(call normalize-path-list,$(PRIVATE_SRC_FILES)) \
+ -o $@ \
+ $(PRIVATE_FLAGS)
+
+LOCAL_PREBUILT_MODULE_FILE := $(GEN)
+LOCAL_SRC_FILES :=
+LOCAL_GENERATED_SOURCES :=
+
+LOCAL_ADD_VBMETA_VERSION :=
+LOCAL_ASSEMBLE_VINTF_ENV_VARS :=
+LOCAL_ASSEMBLE_VINTF_FLAGS :=
+LOCAL_KERNEL_VERSIONS :=
+LOCAL_GEN_FILE_DEPENDENCIES :=
+my_matrix_src_files :=
+
+include $(BUILD_PREBUILT)
+
+remove-minor-revision :=
diff --git a/confirmationui/1.0/Android.bp b/confirmationui/1.0/Android.bp
new file mode 100644
index 0000000..21acecb
--- /dev/null
+++ b/confirmationui/1.0/Android.bp
@@ -0,0 +1,27 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.confirmationui@1.0",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IConfirmationResultCallback.hal",
+ "IConfirmationUI.hal",
+ ],
+ interfaces: [
+ "android.hardware.keymaster@4.0",
+ "android.hidl.base@1.0",
+ ],
+ types: [
+ "MessageSize",
+ "ResponseCode",
+ "TestKeyBits",
+ "TestModeCommands",
+ "UIOption",
+ ],
+ gen_java: false,
+}
+
diff --git a/confirmationui/1.0/IConfirmationResultCallback.hal b/confirmationui/1.0/IConfirmationResultCallback.hal
new file mode 100644
index 0000000..03a10cf
--- /dev/null
+++ b/confirmationui/1.0/IConfirmationResultCallback.hal
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.confirmationui@1.0;
+
+/**
+ * Callback interface passed to IConfirmationUI::promptUserConfirmation().
+ * Informs the caller about the result of the prompt operation.
+ */
+interface IConfirmationResultCallback {
+ /**
+ * This callback is called by the confirmation provider when it stops prompting the user.
+ * Iff the user has confirmed the prompted text, error is ErrorCode::OK and the
+ * parameters formattedMessage and confirmationToken hold the values needed to request
+ * a signature from keymaster.
+ * In all other cases formattedMessage and confirmationToken must be of length 0.
+ *
+ * @param error - OK: IFF the user has confirmed the prompt.
+ * - Canceled: If the user has pressed the cancel button.
+ * - Aborted: If IConfirmationUI::abort() was called.
+ * - SystemError: If an unexpected System error occurred that prevented the TUI
+ * from being shut down gracefully.
+ * @param formattedMessage holds the prompt text and extra data.
+ * The message is CBOR (RFC 7049) encoded and has the following format:
+ * CBOR_MAP{ "prompt", <promptText>, "extra", <extraData> }
+ * The message is a CBOR encoded map (type 5) with the keys
+ * "prompt" and "extra". The keys are encoded as CBOR text string
+ * (type 3). The value <promptText> is encoded as CBOR text string
+ * (type 3), and the value <extraData> is encoded as CBOR byte string
+ * (type 2). The map must have exactly one key value pair for each of
+ * the keys "prompt" and "extra". Other keys are not allowed.
+ * The value of "prompt" is given by the proptText argument to
+ * IConfirmationUI::promptUserConfirmation and must not be modified
+ * by the implementation.
+ * The value of "extra" is given by the extraData argument to
+ * IConfirmationUI::promptUserConfirmation and must not be modified
+ * or interpreted by the implementation.
+ *
+ * @param confirmationToken a 32-byte HMAC-SHA256 value, computed over
+ * "confirmation token" || <formattedMessage>
+ * i.e. the literal UTF-8 encoded string "confirmation token", without
+ * the "", concatenated with the formatted message as returned in the
+ * formattedMessage argument. The HMAC is keyed with a 256-bit secret
+ * which is shared with Keymaster. In test mode the test key MUST be
+ * used (see types.hal TestModeCommands and TestKeyBits).
+ */
+ result(ResponseCode error, vec<uint8_t> formattedMessage, vec<uint8_t> confirmationToken);
+};
diff --git a/confirmationui/1.0/IConfirmationUI.hal b/confirmationui/1.0/IConfirmationUI.hal
new file mode 100644
index 0000000..db8055d
--- /dev/null
+++ b/confirmationui/1.0/IConfirmationUI.hal
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.confirmationui@1.0;
+
+import android.hardware.keymaster@4.0::HardwareAuthToken;
+import IConfirmationResultCallback;
+
+interface IConfirmationUI {
+ /**
+ * Asynchronously initiates a confirmation UI dialog prompting the user to confirm a given text.
+ * The TUI prompt must be implemented in such a way that a positive response indicates with
+ * high confidence that a user has seen the given prompt text even if the Android framework
+ * including the kernel was compromised.
+ *
+ * @param resultCB Implementation of IResultCallback. Used by the implementation to report
+ * the result of the current pending user prompt.
+ *
+ * @param promptText UTF-8 encoded string which is to be presented to the user.
+ *
+ * @param extraData A binary blob that must be included in the formatted output message as is.
+ * It is opaque to the implementation. Implementations must neither interpret
+ * nor modify the content.
+ *
+ * @param locale String specifying the locale that must be used by the TUI dialog. The string
+ * is an IETF BCP 47 tag.
+ *
+ * @param uiOptions A set of uiOptions manipulating how the confirmation prompt is displayed.
+ * Refer to UIOption in types.hal for possible options.
+ *
+ * @return error - OK: IFF the dialog was successfully started. In this case, and only in this
+ * case, the implementation must, eventually, call the callback to
+ * indicate completion.
+ * - OperationPending: Is returned when the confirmation provider is currently
+ * in use.
+ * - SystemError: An error occurred trying to communicate with the confirmation
+ * provider (e.g. trusted app).
+ * - UIError: The confirmation provider encountered an issue with displaying
+ * the prompt text to the user.
+ */
+ promptUserConfirmation(IConfirmationResultCallback resultCB, string promptText,
+ vec<uint8_t> extraData, string locale, vec<UIOption> uiOptions)
+ generates(ResponseCode error);
+
+ /**
+ * DeliverSecureInput is used by the framework to deliver a secure input event to the
+ * confirmation provider.
+ *
+ * VTS test mode:
+ * This function can be used to test certain code paths non-interactively. See TestModeCommands
+ * in types.hal for details.
+ *
+ * @param secureInputToken An authentication token as generated by Android authentication
+ * providers.
+ *
+ * @return error - Ignored: Unless used for testing (See TestModeCommands).
+ */
+ deliverSecureInputEvent(HardwareAuthToken secureInputToken)
+ generates(ResponseCode error);
+
+ /**
+ * Aborts a pending user prompt. This allows the framework to gracefully end a TUI dialog.
+ * If a TUI operation was pending the corresponding call back is informed with
+ * ErrorCode::Aborted.
+ */
+ abort();
+};
+
diff --git a/confirmationui/1.0/default/Android.bp b/confirmationui/1.0/default/Android.bp
new file mode 100644
index 0000000..10018e8
--- /dev/null
+++ b/confirmationui/1.0/default/Android.bp
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+ name: "android.hardware.confirmationui@1.0-service",
+ init_rc: ["android.hardware.confirmationui@1.0-service.rc"],
+ vendor: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "service.cpp",
+ "ConfirmationUI.cpp",
+ "PlatformSpecifics.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.confirmationui-support-lib",
+ "android.hardware.keymaster@4.0",
+ "libcrypto",
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+}
\ No newline at end of file
diff --git a/confirmationui/1.0/default/ConfirmationUI.cpp b/confirmationui/1.0/default/ConfirmationUI.cpp
new file mode 100644
index 0000000..f241a76
--- /dev/null
+++ b/confirmationui/1.0/default/ConfirmationUI.cpp
@@ -0,0 +1,66 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "ConfirmationUI.h"
+
+#include "PlatformSpecifics.h"
+
+#include <android/hardware/confirmationui/support/cbor.h>
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+
+#include <android/hardware/confirmationui/1.0/generic/GenericOperation.h>
+
+#include <time.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::confirmationui::V1_0::generic::Operation;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+
+uint8_t hmacKey[32];
+
+// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow.
+Return<ResponseCode> ConfirmationUI::promptUserConfirmation(
+ const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) {
+ auto& operation = MyOperation::get();
+ return operation.init(resultCB, promptText, extraData, locale, uiOptions);
+}
+
+Return<ResponseCode> ConfirmationUI::deliverSecureInputEvent(
+ const HardwareAuthToken& secureInputToken) {
+ auto& operation = MyOperation::get();
+ return operation.deliverSecureInputEvent(secureInputToken);
+}
+
+Return<void> ConfirmationUI::abort() {
+ auto& operation = MyOperation::get();
+ operation.abort();
+ operation.finalize(hmacKey);
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/confirmationui/1.0/default/ConfirmationUI.h b/confirmationui/1.0/default/ConfirmationUI.h
new file mode 100644
index 0000000..e9e7f99
--- /dev/null
+++ b/confirmationui/1.0/default/ConfirmationUI.h
@@ -0,0 +1,57 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H
+#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H
+
+#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+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;
+using ::android::sp;
+
+struct ConfirmationUI : public IConfirmationUI {
+ // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow.
+ Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
+ const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData,
+ const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) override;
+ Return<ResponseCode> deliverSecureInputEvent(
+ const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
+ Return<void> abort() override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H
diff --git a/confirmationui/1.0/default/OWNERS b/confirmationui/1.0/default/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/confirmationui/1.0/default/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/confirmationui/1.0/default/PlatformSpecifics.cpp b/confirmationui/1.0/default/PlatformSpecifics.cpp
new file mode 100644
index 0000000..dd039e2
--- /dev/null
+++ b/confirmationui/1.0/default/PlatformSpecifics.cpp
@@ -0,0 +1,62 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "PlatformSpecifics.h"
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+#include <time.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+MonotonicClockTimeStamper::TimeStamp MonotonicClockTimeStamper::now() {
+ timespec ts;
+ if (!clock_gettime(CLOCK_BOOTTIME, &ts)) {
+ return TimeStamp(ts.tv_sec * UINT64_C(1000) + ts.tv_nsec / UINT64_C(1000000));
+ } else {
+ return {};
+ }
+}
+
+support::NullOr<support::array<uint8_t, 32>> HMacImplementation::hmac256(
+ const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers) {
+ HMAC_CTX hmacCtx;
+ HMAC_CTX_init(&hmacCtx);
+ if (!HMAC_Init_ex(&hmacCtx, key, 32, EVP_sha256(), nullptr)) {
+ return {};
+ }
+ for (auto& buffer : buffers) {
+ if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+ return {};
+ }
+ }
+ support::array<uint8_t, 32> result;
+ if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+ return {};
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/confirmationui/1.0/default/PlatformSpecifics.h b/confirmationui/1.0/default/PlatformSpecifics.h
new file mode 100644
index 0000000..18b88c8
--- /dev/null
+++ b/confirmationui/1.0/default/PlatformSpecifics.h
@@ -0,0 +1,64 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_
+#define CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_
+
+#include <stdint.h>
+#include <time.h>
+
+#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
+#include <android/hardware/confirmationui/1.0/generic/GenericOperation.h>
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace implementation {
+
+struct MonotonicClockTimeStamper {
+ class TimeStamp {
+ public:
+ explicit TimeStamp(uint64_t ts) : timestamp_(ts), ok_(true) {}
+ TimeStamp() : timestamp_(0), ok_(false) {}
+ bool isOk() const { return ok_; }
+ operator const uint64_t() const { return timestamp_; }
+
+ private:
+ uint64_t timestamp_;
+ bool ok_;
+ };
+ static TimeStamp now();
+};
+
+class HMacImplementation {
+ public:
+ static support::NullOr<support::array<uint8_t, 32>> hmac256(
+ const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers);
+};
+
+using MyOperation = generic::Operation<sp<IConfirmationResultCallback>, MonotonicClockTimeStamper,
+ HMacImplementation>;
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_
diff --git a/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc b/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc
new file mode 100644
index 0000000..a278028
--- /dev/null
+++ b/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service
+ class hal
+ user system
+ group system drmrpc
diff --git a/confirmationui/1.0/default/service.cpp b/confirmationui/1.0/default/service.cpp
new file mode 100644
index 0000000..58ec66a
--- /dev/null
+++ b/confirmationui/1.0/default/service.cpp
@@ -0,0 +1,38 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "android.hardware.confirmationui@1.0-service"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "ConfirmationUI.h"
+
+using android::hardware::joinRpcThreadpool;
+
+using android::hardware::confirmationui::V1_0::implementation::ConfirmationUI;
+
+int main() {
+ auto confirmationui = new ConfirmationUI();
+ auto status = confirmationui->registerAsService();
+ if (status != android::OK) {
+ LOG(FATAL) << "Could not register service for ConfirmationIU 1.0 (" << status << ")";
+ }
+
+ joinRpcThreadpool();
+ return -1; // Should never get here.
+}
diff --git a/confirmationui/1.0/types.hal b/confirmationui/1.0/types.hal
new file mode 100644
index 0000000..fd7ae6a
--- /dev/null
+++ b/confirmationui/1.0/types.hal
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.confirmationui@1.0;
+
+/**
+ * UI modification options.
+ */
+enum UIOption : uint32_t {
+ /** Accessibility: Requests color inverted style. */
+ AccessibilityInverted = 0,
+ /** Accessibility: Requests magnified style. */
+ AccessibilityMagnified = 1,
+};
+
+/**
+ * Codes returned by ConfirmationUI API calls.
+ */
+enum ResponseCode : uint32_t {
+ /** API call succeeded or the user gave approval (result callback). */
+ OK = 0,
+ /** The user canceled the TUI (result callback). */
+ Canceled = 1,
+ /** IConfirmationUI::abort() was called. (result callback). */
+ Aborted = 2,
+ /** Cannot start another prompt. */
+ OperationPending = 3,
+ /** IConfirmationUI::deliverSecureInputEvent call was ingored. */
+ Ignored = 4,
+ /** An unexpected system error occured. */
+ SystemError = 5,
+ /** Returned by an unimplemented API call. */
+ Unimplemented = 6,
+ /**
+ * This is returned when an error is diagnosed that should have been
+ * caught by earlier input sanitization. Should never be seen in production.
+ */
+ Unexpected = 7,
+ /** General UI error. */
+ UIError = 0x10000,
+ UIErrorMissingGlyph,
+ /**
+ * The implementation must return this error code on promptUserConfirmation if the
+ * resulting formatted message does not fit into MessageSize::MAX bytes. It is
+ * advised that the implementation formats the message upon receiving this API call to
+ * be able to diagnose this syndrome.
+ */
+ UIErrorMessageTooLong,
+ UIErrorMalformedUTF8Encoding,
+};
+
+/**
+ * This defines the maximum message size. This indirectly limits the size of the prompt text
+ * and the extra data that can be passed to the confirmation UI. The prompt text and extra data
+ * must fit in to this size including CBOR header information.
+ */
+enum MessageSize : uint32_t { MAX = 0x1800 };
+
+/**
+ * The test key is 32byte word with all bytes set to TestKeyBits::BYTE.
+ */
+enum TestKeyBits: uint8_t { BYTE = 0xA5 };
+
+/**
+ * Test mode commands.
+ *
+ * IConfirmationUI::deliverSecureInputEvent can be used to test certain code paths.
+ * To that end, the caller passes an auth token that has an HMAC keyed with the test key
+ * (see TestKeyBits in types.hal). Implementations first check the HMAC against test key.
+ * If the test key produces a matching HMAC, the implementation evaluates the challenge field
+ * of the auth token against the values defined in TestModeCommand.
+ * If the command indicates that a confirmation token is to be generated the test key MUST be used
+ * to generate this confirmation token.
+ *
+ * See command code for individual test command descriptions.
+ */
+enum TestModeCommands: uint64_t {
+ /**
+ * Simulates the user pressing the OK button on the UI. If no operation is pending
+ * ResponseCode::Ignored must be returned. A pending operation is finalized successfully
+ * see IConfirmationResultCallback::result, however, the test key (see TestKeyBits) MUST be
+ * used to generate the confirmation token.
+ */
+ OK_EVENT = 0,
+ /**
+ * Simulates the user pressing the CANCEL button on the UI. If no operation is pending
+ * Result::Ignored must be returned. A pending operation is finalized as specified in
+ * IConfirmationResultCallback.hal.
+ */
+ CANCEL_EVENT = 1,
+};
diff --git a/confirmationui/support/Android.bp b/confirmationui/support/Android.bp
new file mode 100644
index 0000000..62156b3
--- /dev/null
+++ b/confirmationui/support/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+ name: "android.hardware.confirmationui-support-lib",
+ vendor_available: true,
+ host_supported: true,
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "src/cbor.cpp",
+ "src/confirmationui_utils.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ]
+}
+
+cc_test {
+ name: "android.hardware.confirmationui-support-lib-tests",
+ srcs: [
+ "test/gtest_main.cpp",
+ "test/android_cbor_test.cpp",
+ "test/msg_formatting_test.cpp",
+ ],
+ static_libs: [
+ "libgtest",
+ "android.hardware.confirmationui-support-lib",
+ ],
+ shared_libs: [
+ "android.hardware.confirmationui@1.0",
+ "android.hardware.keymaster@4.0",
+ "libhidlbase",
+ ],
+ clang: true,
+ cflags: [ "-O0" ],
+}
diff --git a/confirmationui/support/OWNERS b/confirmationui/support/OWNERS
new file mode 100644
index 0000000..335660d
--- /dev/null
+++ b/confirmationui/support/OWNERS
@@ -0,0 +1,2 @@
+jdanis@google.com
+swillden@google.com
diff --git a/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h
new file mode 100644
index 0000000..a88cd40
--- /dev/null
+++ b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h
@@ -0,0 +1,188 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
+#define CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
+
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/confirmationui/support/cbor.h>
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+#include <android/hardware/keymaster/4.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace V1_0 {
+namespace generic {
+
+namespace {
+using namespace ::android::hardware::confirmationui::support;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+
+inline bool hasOption(UIOption option, const hidl_vec<UIOption>& uiOptions) {
+ for (auto& o : uiOptions) {
+ if (o == option) return true;
+ }
+ return false;
+}
+
+template <typename Callback, typename TimeStamper, typename HmacImplementation>
+class Operation {
+ using HMacer = support::HMac<HmacImplementation>;
+
+ public:
+ Operation() : error_(ResponseCode::Ignored), formattedMessageLength_(0) {}
+
+ ResponseCode init(const Callback& resultCB, const hidl_string& promptText,
+ const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
+ const hidl_vec<UIOption>& uiOptions) {
+ (void)locale;
+ (void)uiOptions;
+ resultCB_ = resultCB;
+ if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending;
+ // TODO make copy of promptText before using it may reside in shared buffer
+ auto state = write(
+ WriteState(formattedMessageBuffer_),
+ map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData))));
+ switch (state.error_) {
+ case Error::OK:
+ break;
+ case Error::OUT_OF_DATA:
+ return ResponseCode::UIErrorMessageTooLong;
+ case Error::MALFORMED_UTF8:
+ return ResponseCode::UIErrorMalformedUTF8Encoding;
+ case Error::MALFORMED:
+ default:
+ return ResponseCode::Unexpected;
+ }
+ formattedMessageLength_ = state.data_ - formattedMessageBuffer_;
+ // setup TUI and diagnose more UI errors here.
+ // on success record the start time
+ startTime_ = TimeStamper::now();
+ if (!startTime_.isOk()) {
+ return ResponseCode::SystemError;
+ }
+ error_ = ResponseCode::OK;
+ return ResponseCode::OK;
+ }
+
+ void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; }
+
+ void abort() {
+ // tear down TUI here
+ if (isPending()) {
+ resultCB_->result(ResponseCode::Aborted, {}, {});
+ error_ = ResponseCode::Ignored;
+ }
+ }
+
+ void userCancel() {
+ // tear down TUI here
+ if (isPending()) error_ = ResponseCode::Canceled;
+ }
+
+ void finalize(const uint8_t key[32]) {
+ if (error_ == ResponseCode::Ignored) return;
+ resultCB_->result(error_, getMessage(), userConfirm(key));
+ error_ = ResponseCode::Ignored;
+ resultCB_ = {};
+ }
+
+ bool isPending() const { return error_ != ResponseCode::Ignored; }
+
+ static Operation& get() {
+ static Operation operation;
+ return operation;
+ }
+
+ ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
+ constexpr uint8_t testKeyByte = static_cast<uint8_t>(TestKeyBits::BYTE);
+ constexpr uint8_t testKey[32] = {testKeyByte, testKeyByte, testKeyByte, testKeyByte,
+ testKeyByte, testKeyByte, testKeyByte, testKeyByte,
+ testKeyByte, testKeyByte, testKeyByte, testKeyByte,
+ testKeyByte, testKeyByte, testKeyByte, testKeyByte};
+
+ auto hmac = HMacer::hmac256(testKey, "\0", bytes_cast(secureInputToken.challenge),
+ bytes_cast(secureInputToken.userId),
+ bytes_cast(secureInputToken.authenticatorId),
+ bytes_cast(hton(secureInputToken.authenticatorType)),
+ bytes_cast(hton(secureInputToken.timestamp)));
+ if (!hmac.isOk()) return ResponseCode::Unexpected;
+ if (hmac.value() == secureInputToken.mac) {
+ // okay so this is a test token
+ switch (static_cast<TestModeCommands>(secureInputToken.challenge)) {
+ case TestModeCommands::OK_EVENT: {
+ if (isPending()) {
+ finalize(testKey);
+ return ResponseCode::OK;
+ } else {
+ return ResponseCode::Ignored;
+ }
+ }
+ case TestModeCommands::CANCEL_EVENT: {
+ bool ignored = !isPending();
+ userCancel();
+ finalize(testKey);
+ return ignored ? ResponseCode::Ignored : ResponseCode::OK;
+ }
+ default:
+ return ResponseCode::Ignored;
+ }
+ }
+ return ResponseCode::Ignored;
+ }
+
+ private:
+ bool acceptAuthToken(const HardwareAuthToken&) { return false; }
+ hidl_vec<uint8_t> getMessage() {
+ hidl_vec<uint8_t> result;
+ if (error_ != ResponseCode::OK) return {};
+ result.setToExternal(formattedMessageBuffer_, formattedMessageLength_);
+ return result;
+ }
+ hidl_vec<uint8_t> userConfirm(const uint8_t key[32]) {
+ // tear down TUI here
+ if (error_ != ResponseCode::OK) return {};
+ confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage());
+ if (!confirmationTokenScratchpad_.isOk()) {
+ error_ = ResponseCode::Unexpected;
+ return {};
+ }
+ hidl_vec<uint8_t> result;
+ result.setToExternal(confirmationTokenScratchpad_->data(),
+ confirmationTokenScratchpad_->size());
+ return result;
+ }
+
+ ResponseCode error_;
+ uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)];
+ size_t formattedMessageLength_;
+ NullOr<array<uint8_t, 32>> confirmationTokenScratchpad_;
+ Callback resultCB_;
+ typename TimeStamper::TimeStamp startTime_;
+ NullOr<array<uint8_t, 32>> hmacKey_;
+};
+
+} // namespace
+} // namespace generic
+} // namespace V1_0
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_
diff --git a/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h b/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h
new file mode 100644
index 0000000..f5814d4
--- /dev/null
+++ b/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h
@@ -0,0 +1,335 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
+#define CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <type_traits>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace support {
+
+template <typename In, typename Out>
+Out copy(In begin, In end, Out out) {
+ while (begin != end) {
+ *out++ = *begin++;
+ }
+ return out;
+}
+
+enum class Type : uint8_t {
+ NUMBER = 0,
+ NEGATIVE = 1,
+ BYTE_STRING = 2,
+ TEXT_STRING = 3,
+ ARRAY = 4,
+ MAP = 5,
+ TAG = 6,
+ FLOAT = 7,
+};
+
+enum class Error : uint32_t {
+ OK = 0,
+ OUT_OF_DATA = 1,
+ MALFORMED = 2,
+ MALFORMED_UTF8 = 3,
+};
+
+template <typename Key, typename Value>
+struct MapElement {
+ const Key& key_;
+ const Value& value_;
+ MapElement(const Key& key, const Value& value) : key_(key), value_(value) {}
+};
+
+template <typename... Elems>
+struct Array;
+
+template <typename Head, typename... Tail>
+struct Array<Head, Tail...> {
+ const Head& head_;
+ Array<Tail...> tail_;
+ Array(const Head& head, const Tail&... tail) : head_(head), tail_(tail...) {}
+ constexpr size_t size() const { return sizeof...(Tail) + 1; };
+};
+
+template <>
+struct Array<> {};
+
+struct TextStr {};
+struct ByteStr {};
+
+template <typename T, typename Variant>
+struct StringBuffer {
+ const T* data_;
+ size_t size_;
+ StringBuffer(const T* data, size_t size) : data_(data), size_(size) {
+ static_assert(sizeof(T) == 1, "elements too large");
+ }
+ const T* data() const { return data_; }
+ size_t size() const { return size_; }
+};
+
+/**
+ * Takes a char array turns it into a StringBuffer of TextStr type. The length of the resulting
+ * StringBuffer is size - 1, effectively stripping the 0 character from the region being considered.
+ * If the terminating 0 shall not be stripped use text_keep_last.
+ */
+template <size_t size>
+StringBuffer<char, TextStr> text(const char (&str)[size]) {
+ if (size > 0) return StringBuffer<char, TextStr>(str, size - 1);
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+/**
+ * As opposed to text(const char (&str)[size] this function does not strips the last character.
+ */
+template <size_t size>
+StringBuffer<char, TextStr> text_keep_last(const char (&str)[size]) {
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+template <typename T>
+auto getData(const T& v) -> decltype(v.data()) {
+ return v.data();
+}
+
+template <typename T>
+auto getData(const T& v) -> decltype(v.c_str()) {
+ return v.c_str();
+}
+
+template <typename T>
+auto text(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr> {
+ return StringBuffer<std::decay_t<decltype(*getData(str))>, TextStr>(getData(str), str.size());
+}
+
+inline StringBuffer<char, TextStr> text(const char* str, size_t size) {
+ return StringBuffer<char, TextStr>(str, size);
+}
+
+template <typename T, size_t size>
+StringBuffer<T, ByteStr> bytes(const T (&str)[size]) {
+ return StringBuffer<T, ByteStr>(str, size);
+}
+
+template <typename T>
+StringBuffer<T, ByteStr> bytes(const T* str, size_t size) {
+ return StringBuffer<T, ByteStr>(str, size);
+}
+
+template <typename T>
+auto bytes(const T& str) -> StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr> {
+ return StringBuffer<std::decay_t<decltype(*getData(str))>, ByteStr>(getData(str), str.size());
+}
+
+template <typename... Elems>
+struct Map;
+
+template <typename HeadKey, typename HeadValue, typename... Tail>
+struct Map<MapElement<HeadKey, HeadValue>, Tail...> {
+ const MapElement<HeadKey, HeadValue>& head_;
+ Map<Tail...> tail_;
+ Map(const MapElement<HeadKey, HeadValue>& head, const Tail&... tail)
+ : head_(head), tail_(tail...) {}
+ constexpr size_t size() const { return sizeof...(Tail) + 1; };
+};
+
+template <>
+struct Map<> {};
+
+template <typename... Keys, typename... Values>
+Map<MapElement<Keys, Values>...> map(const MapElement<Keys, Values>&... elements) {
+ return Map<MapElement<Keys, Values>...>(elements...);
+}
+
+template <typename... Elements>
+Array<Elements...> arr(const Elements&... elements) {
+ return Array<Elements...>(elements...);
+}
+
+template <typename Key, typename Value>
+MapElement<Key, Value> pair(const Key& k, const Value& v) {
+ return MapElement<Key, Value>(k, v);
+}
+
+template <size_t size>
+struct getUnsignedType;
+
+template <>
+struct getUnsignedType<sizeof(uint8_t)> {
+ typedef uint8_t type;
+};
+template <>
+struct getUnsignedType<sizeof(uint16_t)> {
+ typedef uint16_t type;
+};
+template <>
+struct getUnsignedType<sizeof(uint32_t)> {
+ typedef uint32_t type;
+};
+template <>
+struct getUnsignedType<sizeof(uint64_t)> {
+ typedef uint64_t type;
+};
+
+template <size_t size>
+using Unsigned = typename getUnsignedType<size>::type;
+
+class WriteState {
+ public:
+ WriteState() : data_(nullptr), size_(0), error_(Error::OK) {}
+ WriteState(uint8_t* buffer, size_t size) : data_(buffer), size_(size), error_(Error::OK) {}
+ WriteState(uint8_t* buffer, size_t size, Error error)
+ : data_(buffer), size_(size), error_(error) {}
+ template <size_t size>
+ WriteState(uint8_t (&buffer)[size]) : data_(buffer), size_(size), error_(Error::OK) {}
+
+ WriteState& operator++() {
+ if (size_) {
+ ++data_;
+ --size_;
+ } else {
+ error_ = Error::OUT_OF_DATA;
+ }
+ return *this;
+ }
+ WriteState& operator+=(size_t offset) {
+ if (offset > size_) {
+ error_ = Error::OUT_OF_DATA;
+ } else {
+ data_ += offset;
+ size_ -= offset;
+ }
+ return *this;
+ }
+ operator bool() const { return error_ == Error::OK; }
+
+ uint8_t* data_;
+ size_t size_;
+ Error error_;
+};
+
+WriteState writeHeader(WriteState wState, Type type, const uint64_t value);
+bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out);
+
+template <typename T>
+WriteState writeNumber(WriteState wState, const T& v) {
+ if (!wState) return wState;
+ if (v >= 0) {
+ return writeHeader(wState, Type::NUMBER, v);
+ } else {
+ return writeHeader(wState, Type::NEGATIVE, UINT64_C(-1) - v);
+ }
+}
+
+inline WriteState write(const WriteState& wState, const uint8_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int8_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint16_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int16_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint32_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int32_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const uint64_t& v) {
+ return writeNumber(wState, v);
+}
+inline WriteState write(const WriteState& wState, const int64_t& v) {
+ return writeNumber(wState, v);
+}
+
+template <typename T>
+WriteState write(WriteState wState, const StringBuffer<T, TextStr>& v) {
+ wState = writeHeader(wState, Type::TEXT_STRING, v.size());
+ uint8_t* buffer = wState.data_;
+ wState += v.size();
+ if (!wState) return wState;
+ if (!checkUTF8Copy(v.data(), v.data() + v.size(), buffer)) {
+ wState.error_ = Error::MALFORMED_UTF8;
+ }
+ return wState;
+}
+
+template <typename T>
+WriteState write(WriteState wState, const StringBuffer<T, ByteStr>& v) {
+ wState = writeHeader(wState, Type::BYTE_STRING, v.size());
+ uint8_t* buffer = wState.data_;
+ wState += v.size();
+ if (!wState) return wState;
+ static_assert(sizeof(*v.data()) == 1, "elements too large");
+ copy(v.data(), v.data() + v.size(), buffer);
+ return wState;
+}
+
+template <template <typename...> class Arr>
+WriteState writeArrayHelper(WriteState wState, const Arr<>&) {
+ return wState;
+}
+
+template <template <typename...> class Arr, typename Head, typename... Tail>
+WriteState writeArrayHelper(WriteState wState, const Arr<Head, Tail...>& arr) {
+ wState = write(wState, arr.head_);
+ return writeArrayHelper(wState, arr.tail_);
+}
+
+template <typename... Elems>
+WriteState write(WriteState wState, const Map<Elems...>& map) {
+ if (!wState) return wState;
+ wState = writeHeader(wState, Type::MAP, map.size());
+ return writeArrayHelper(wState, map);
+}
+
+template <typename... Elems>
+WriteState write(WriteState wState, const Array<Elems...>& arr) {
+ if (!wState) return wState;
+ wState = writeHeader(wState, Type::ARRAY, arr.size());
+ return writeArrayHelper(wState, arr);
+}
+
+template <typename Key, typename Value>
+WriteState write(WriteState wState, const MapElement<Key, Value>& element) {
+ if (!wState) return wState;
+ wState = write(wState, element.key_);
+ return write(wState, element.value_);
+}
+
+template <typename Head, typename... Tail>
+WriteState write(WriteState wState, const Head& head, const Tail&... tail) {
+ wState = write(wState, head);
+ return write(wState, tail...);
+}
+
+} // namespace support
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_
diff --git a/confirmationui/support/include/android/hardware/confirmationui/support/confirmationui_utils.h b/confirmationui/support/include/android/hardware/confirmationui/support/confirmationui_utils.h
new file mode 100644
index 0000000..d551433
--- /dev/null
+++ b/confirmationui/support/include/android/hardware/confirmationui/support/confirmationui_utils.h
@@ -0,0 +1,213 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
+#define CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <algorithm>
+#include <initializer_list>
+#include <type_traits>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace support {
+
+/**
+ * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out
+ * of band. Note that if the wrapped value is a reference it is unsafe to access the value if
+ * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the
+ * wrapped value. In this case the pointer will be NULL though, and the value will be default
+ * constructed.
+ */
+template <typename ValueT>
+class NullOr {
+ template <typename T>
+ struct reference_initializer {
+ static T&& init() { return *static_cast<std::remove_reference_t<T>*>(nullptr); }
+ };
+ template <typename T>
+ struct pointer_initializer {
+ static T init() { return nullptr; }
+ };
+ template <typename T>
+ struct value_initializer {
+ static T init() { return T(); }
+ };
+ template <typename T>
+ using initializer_t =
+ std::conditional_t<std::is_lvalue_reference<T>::value, reference_initializer<T>,
+ std::conditional_t<std::is_pointer<T>::value, pointer_initializer<T>,
+ value_initializer<T>>>;
+
+ public:
+ NullOr() : value_(initializer_t<ValueT>::init()), null_(true) {}
+ NullOr(ValueT&& value) : value_(std::forward<ValueT>(value)), null_(false) {}
+
+ bool isOk() const { return !null_; }
+
+ const ValueT& value() const & { return value_; }
+ ValueT& value() & { return value_; }
+ ValueT&& value() && { return std::move(value_); }
+
+ const std::remove_reference_t<ValueT>* operator->() const { return &value_; }
+ std::remove_reference_t<ValueT>* operator->() { return &value_; }
+
+ private:
+ ValueT value_;
+ bool null_;
+};
+
+template <typename T, size_t elements>
+class array {
+ using array_type = T[elements];
+
+ public:
+ array() : data_{} {}
+ array(const T (&data)[elements]) { std::copy(data, data + elements, data_); }
+
+ T* data() { return data_; }
+ const T* data() const { return data_; }
+ constexpr size_t size() const { return elements; }
+ operator const array_type&() const { return data_; }
+
+ T* begin() { return data_; }
+ T* end() { return data_ + elements; }
+ const T* begin() const { return data_; }
+ const T* end() const { return data_ + elements; }
+
+ private:
+ array_type data_;
+};
+
+template <typename T>
+auto bytes_cast(const T& v) -> const uint8_t (&)[sizeof(T)] {
+ return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
+}
+template <typename T>
+auto bytes_cast(T& v) -> uint8_t (&)[sizeof(T)] {
+ return *reinterpret_cast<uint8_t(*)[sizeof(T)]>(&v);
+}
+
+class ByteBufferProxy {
+ template <typename T>
+ struct has_data {
+ template <typename U>
+ static int f(const U*, const void*) {
+ return 0;
+ }
+ template <typename U>
+ static int* f(const U* u, decltype(u->data())) {
+ return nullptr;
+ }
+ static constexpr bool value = std::is_pointer<decltype(f((T*)nullptr, ""))>::value;
+ };
+
+ public:
+ template <typename T>
+ ByteBufferProxy(const T& buffer, decltype(buffer.data()) = nullptr)
+ : data_(reinterpret_cast<const uint8_t*>(buffer.data())), size_(buffer.size()) {
+ static_assert(sizeof(decltype(*buffer.data())) == 1, "elements to large");
+ }
+
+ // this overload kicks in for types that have .c_str() but not .data(), such as hidl_string.
+ // std::string has both so we need to explicitly disable this overload if .data() is present.
+ template <typename T>
+ ByteBufferProxy(const T& buffer,
+ std::enable_if_t<!has_data<T>::value, decltype(buffer.c_str())> = nullptr)
+ : data_(reinterpret_cast<const uint8_t*>(buffer.c_str())), size_(buffer.size()) {
+ static_assert(sizeof(decltype(*buffer.c_str())) == 1, "elements to large");
+ }
+
+ template <size_t size>
+ ByteBufferProxy(const char (&buffer)[size])
+ : data_(reinterpret_cast<const uint8_t*>(buffer)), size_(size - 1) {
+ static_assert(size > 0, "even an empty string must be 0-terminated");
+ }
+
+ template <size_t size>
+ ByteBufferProxy(const uint8_t (&buffer)[size]) : data_(buffer), size_(size) {}
+
+ ByteBufferProxy() : data_(nullptr), size_(0) {}
+
+ const uint8_t* data() const { return data_; }
+ size_t size() const { return size_; }
+
+ const uint8_t* begin() const { return data_; }
+ const uint8_t* end() const { return data_ + size_; }
+
+ private:
+ const uint8_t* data_;
+ size_t size_;
+};
+
+/**
+ * Implementer are expected to provide an implementation with the following prototype:
+ * static NullOr<array<uint8_t, 32>> hmac256(const uint8_t key[32],
+ * std::initializer_list<ByteBufferProxy> buffers);
+ */
+template <typename Impl>
+class HMac {
+ public:
+ template <typename... Data>
+ static NullOr<array<uint8_t, 32>> hmac256(const uint8_t key[32], const Data&... data) {
+ return Impl::hmac256(key, {data...});
+ }
+};
+
+bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs);
+
+template <typename IntType, uint32_t byteOrder>
+struct choose_hton;
+
+template <typename IntType>
+struct choose_hton<IntType, __ORDER_LITTLE_ENDIAN__> {
+ inline static IntType hton(const IntType& value) {
+ IntType result = {};
+ const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
+ unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
+ for (int i = sizeof(IntType) - 1; i >= 0; --i) {
+ *(outbytes++) = inbytes[i];
+ }
+ return result;
+ }
+};
+
+template <typename IntType>
+struct choose_hton<IntType, __ORDER_BIG_ENDIAN__> {
+ inline static IntType hton(const IntType& value) { return value; }
+};
+
+template <typename IntType>
+inline IntType hton(const IntType& value) {
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
+template <typename IntType>
+inline IntType ntoh(const IntType& value) {
+ // same operation as hton
+ return choose_hton<IntType, __BYTE_ORDER__>::hton(value);
+}
+
+} // namespace support
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // CONFIRMATIONUI_1_0_SUPPORT_INCLUDE_CONFIRMATIONUI_UTILS_H_
diff --git a/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h b/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h
new file mode 100644
index 0000000..0d03591
--- /dev/null
+++ b/confirmationui/support/include/android/hardware/confirmationui/support/msg_formatting.h
@@ -0,0 +1,471 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
+#define CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
+
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/hardware/keymaster/4.0/types.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <algorithm>
+#include <tuple>
+#include <type_traits>
+
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace support {
+
+template <size_t... I>
+class IntegerSequence {};
+
+namespace integer_sequence {
+
+template <typename Lhs, typename Rhs>
+struct conc {};
+
+template <size_t... ILhs, size_t... IRhs>
+struct conc<IntegerSequence<ILhs...>, IntegerSequence<IRhs...>> {
+ using type = IntegerSequence<ILhs..., IRhs...>;
+};
+
+template <typename Lhs, typename Rhs>
+using conc_t = typename conc<Lhs, Rhs>::type;
+
+template <size_t... n>
+struct make {};
+
+template <size_t n>
+struct make<n> {
+ using type = conc_t<typename make<n - 1>::type, IntegerSequence<n - 1>>;
+};
+template <size_t start, size_t n>
+struct make<start, n> {
+ using type = conc_t<typename make<start, n - 1>::type, IntegerSequence<start + n - 1>>;
+};
+
+template <size_t start>
+struct make<start, start> {
+ using type = IntegerSequence<start>;
+};
+
+template <>
+struct make<0> {
+ using type = IntegerSequence<>;
+};
+
+template <size_t... n>
+using make_t = typename make<n...>::type;
+
+} // namespace integer_sequence
+
+template <size_t... idx, typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move_helper(IntegerSequence<idx...>,
+ std::tuple<T...>&& t) {
+ return {std::move(std::get<idx>(t))...};
+}
+
+template <typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>&& t) {
+ return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t));
+}
+
+template <typename... T>
+std::tuple<std::remove_reference_t<T>&&...> tuple_move(std::tuple<T...>& t) {
+ return tuple_move_helper(integer_sequence::make_t<sizeof...(T)>(), std::move(t));
+}
+
+using ::android::hardware::confirmationui::V1_0::ResponseCode;
+using ::android::hardware::confirmationui::V1_0::UIOption;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+template <typename... fields>
+class Message {};
+
+enum class Command : uint32_t {
+ PromptUserConfirmation,
+ DeliverSecureInputEvent,
+ Abort,
+};
+
+template <Command cmd>
+struct Cmd {};
+
+#define DECLARE_COMMAND(cmd) using cmd##_t = Cmd<Command::cmd>
+
+DECLARE_COMMAND(PromptUserConfirmation);
+DECLARE_COMMAND(DeliverSecureInputEvent);
+DECLARE_COMMAND(Abort);
+
+using PromptUserConfirmationMsg = Message<PromptUserConfirmation_t, hidl_string, hidl_vec<uint8_t>,
+ hidl_string, hidl_vec<UIOption>>;
+using PromptUserConfirmationResponse = Message<ResponseCode>;
+using DeliverSecureInputEventMsg = Message<DeliverSecureInputEvent_t, HardwareAuthToken>;
+using DeliverSecureInputEventRespose = Message<ResponseCode>;
+using AbortMsg = Message<Abort_t>;
+using ResultMsg = Message<ResponseCode, hidl_vec<uint8_t>, hidl_vec<uint8_t>>;
+
+template <typename T>
+struct StreamState {
+ using ptr_t = volatile T*;
+ volatile T* pos_;
+ size_t bytes_left_;
+ bool good_;
+ template <size_t size>
+ StreamState(T (&buffer)[size]) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
+ StreamState(T* buffer, size_t size) : pos_(buffer), bytes_left_(size), good_(size > 0) {}
+ StreamState() : pos_(nullptr), bytes_left_(0), good_(false) {}
+ StreamState& operator++() {
+ if (good_ && bytes_left_) {
+ ++pos_;
+ --bytes_left_;
+ } else {
+ good_ = false;
+ }
+ return *this;
+ }
+ StreamState& operator+=(size_t offset) {
+ if (!good_ || offset > bytes_left_) {
+ good_ = false;
+ } else {
+ pos_ += offset;
+ bytes_left_ -= offset;
+ }
+ return *this;
+ }
+ operator bool() const { return good_; }
+ volatile T* pos() const { return pos_; };
+};
+
+using WriteStream = StreamState<uint8_t>;
+using ReadStream = StreamState<const uint8_t>;
+
+inline void zero(volatile uint8_t* begin, const volatile uint8_t* end) {
+ while (begin != end) {
+ *begin++ = 0xaa;
+ }
+}
+inline void zero(const volatile uint8_t*, const volatile uint8_t*) {}
+// This odd alignment function aligns the stream position to a 4byte and never 8byte boundary
+// It is to accommodate the 4 byte size field which is then followed by 8byte alligned data.
+template <typename T>
+StreamState<T> unalign(StreamState<T> s) {
+ uint8_t unalignment = uintptr_t(s.pos_) & 0x3;
+ auto pos = s.pos_;
+ if (unalignment) {
+ s += 4 - unalignment;
+ }
+ // now s.pos_ is aligned on a 4byte boundary
+ if ((uintptr_t(s.pos_) & 0x4) == 0) {
+ // if we are 8byte aligned add 4
+ s += 4;
+ }
+ // zero out the gaps when writing
+ zero(pos, s.pos_);
+ return s;
+}
+
+inline WriteStream write(WriteStream out, const uint8_t* buffer, size_t size) {
+ auto pos = out.pos();
+ uint32_t v = size;
+ out += 4 + size;
+ if (out) {
+ if (size != v) {
+ out.good_ = false;
+ return out;
+ }
+ auto& s = bytes_cast(v);
+ pos = std::copy(s, s + 4, pos);
+ std::copy(buffer, buffer + size, pos);
+ }
+ return out;
+}
+template <size_t size>
+WriteStream write(WriteStream out, const uint8_t (&v)[size]) {
+ return write(out, v, size);
+}
+
+inline std::tuple<ReadStream, ReadStream::ptr_t, size_t> read(ReadStream in) {
+ auto pos = in.pos();
+ in += 4;
+ if (!in) return {in, nullptr, 0};
+ uint32_t size;
+ std::copy(pos, pos + 4, bytes_cast(size));
+ pos = in.pos();
+ in += size;
+ if (!in) return {in, nullptr, 0};
+ return {in, pos, size};
+}
+
+template <typename T>
+std::tuple<ReadStream, T> readSimpleType(ReadStream in) {
+ T result;
+ ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(in, pos, read_size) = read(in);
+ if (!in || read_size != sizeof(T)) {
+ in.good_ = false;
+ return {in, {}};
+ }
+ std::copy(pos, pos + sizeof(T), bytes_cast(result));
+ return {in, std::move(result)};
+}
+
+template <typename T>
+std::tuple<ReadStream, hidl_vec<T>> readSimpleHidlVecInPlace(ReadStream in) {
+ std::tuple<ReadStream, hidl_vec<T>> result;
+ ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(std::get<0>(result), pos, read_size) = read(in);
+ if (!std::get<0>(result) || read_size % sizeof(T)) {
+ std::get<0>(result).good_ = false;
+ return result;
+ }
+ std::get<1>(result).setToExternal(reinterpret_cast<T*>(const_cast<uint8_t*>(pos)),
+ read_size / sizeof(T));
+ return result;
+}
+
+template <typename T>
+WriteStream writeSimpleHidlVec(WriteStream out, const hidl_vec<T>& vec) {
+ return write(out, reinterpret_cast<const uint8_t*>(vec.data()), vec.size() * sizeof(T));
+}
+
+// HardwareAuthToken
+constexpr size_t hatSizeNoMac() {
+ HardwareAuthToken* hat = nullptr;
+ return sizeof hat->challenge + sizeof hat->userId + sizeof hat->authenticatorId +
+ sizeof hat->authenticatorType + sizeof hat->timestamp;
+}
+
+template <typename T>
+inline volatile const uint8_t* copyField(T& field, volatile const uint8_t*(&pos)) {
+ auto& s = bytes_cast(field);
+ std::copy(pos, pos + sizeof(T), s);
+ return pos + sizeof(T);
+}
+inline std::tuple<ReadStream, HardwareAuthToken> read(Message<HardwareAuthToken>, ReadStream in_) {
+ std::tuple<ReadStream, HardwareAuthToken> result;
+ ReadStream& in = std::get<0>(result) = in_;
+ auto& hat = std::get<1>(result);
+ constexpr size_t hatSize = hatSizeNoMac();
+ ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(in, pos, read_size) = read(in);
+ if (!in || read_size != hatSize) {
+ in.good_ = false;
+ return result;
+ }
+ pos = copyField(hat.challenge, pos);
+ pos = copyField(hat.userId, pos);
+ pos = copyField(hat.authenticatorId, pos);
+ pos = copyField(hat.authenticatorType, pos);
+ pos = copyField(hat.timestamp, pos);
+ std::tie(in, hat.mac) = readSimpleHidlVecInPlace<uint8_t>(in);
+ return result;
+}
+
+template <typename T>
+inline volatile uint8_t* copyField(const T& field, volatile uint8_t*(&pos)) {
+ auto& s = bytes_cast(field);
+ return std::copy(s, &s[sizeof(T)], pos);
+}
+
+inline WriteStream write(WriteStream out, const HardwareAuthToken& v) {
+ auto pos = out.pos();
+ uint32_t size_field = hatSizeNoMac();
+ out += 4 + size_field;
+ if (!out) return out;
+ pos = copyField(size_field, pos);
+ pos = copyField(v.challenge, pos);
+ pos = copyField(v.userId, pos);
+ pos = copyField(v.authenticatorId, pos);
+ pos = copyField(v.authenticatorType, pos);
+ pos = copyField(v.timestamp, pos);
+ return writeSimpleHidlVec(out, v.mac);
+}
+
+// ResponseCode
+inline std::tuple<ReadStream, ResponseCode> read(Message<ResponseCode>, ReadStream in) {
+ return readSimpleType<ResponseCode>(in);
+}
+inline WriteStream write(WriteStream out, const ResponseCode& v) {
+ return write(out, bytes_cast(v));
+}
+
+// hidl_vec<uint8_t>
+inline std::tuple<ReadStream, hidl_vec<uint8_t>> read(Message<hidl_vec<uint8_t>>, ReadStream in) {
+ return readSimpleHidlVecInPlace<uint8_t>(in);
+}
+inline WriteStream write(WriteStream out, const hidl_vec<uint8_t>& v) {
+ return writeSimpleHidlVec(out, v);
+}
+
+// hidl_vec<UIOption>
+inline std::tuple<ReadStream, hidl_vec<UIOption>> read(Message<hidl_vec<UIOption>>, ReadStream in) {
+ in = unalign(in);
+ return readSimpleHidlVecInPlace<UIOption>(in);
+}
+inline WriteStream write(WriteStream out, const hidl_vec<UIOption>& v) {
+ out = unalign(out);
+ return writeSimpleHidlVec(out, v);
+}
+
+// hidl_string
+inline std::tuple<ReadStream, hidl_string> read(Message<hidl_string>, ReadStream in) {
+ std::tuple<ReadStream, hidl_string> result;
+ ReadStream& in_ = std::get<0>(result);
+ hidl_string& result_ = std::get<1>(result);
+ ReadStream::ptr_t pos = nullptr;
+ size_t read_size = 0;
+ std::tie(in_, pos, read_size) = read(in);
+ auto terminating_zero = in_.pos();
+ ++in_; // skip the terminating zero. Does nothing if the stream was already bad
+ if (!in_) return result;
+ if (*terminating_zero) {
+ in_.good_ = false;
+ return result;
+ }
+ result_.setToExternal(reinterpret_cast<const char*>(const_cast<const uint8_t*>(pos)),
+ read_size);
+ return result;
+}
+inline WriteStream write(WriteStream out, const hidl_string& v) {
+ out = write(out, reinterpret_cast<const uint8_t*>(v.c_str()), v.size());
+ auto terminating_zero = out.pos();
+ ++out;
+ if (out) {
+ *terminating_zero = 0;
+ }
+ return out;
+}
+
+inline WriteStream write(WriteStream out, Command cmd) {
+ volatile Command* pos = reinterpret_cast<volatile Command*>(out.pos_);
+ out += sizeof(Command);
+ if (out) {
+ *pos = cmd;
+ }
+ return out;
+}
+template <Command cmd>
+WriteStream write(WriteStream out, Cmd<cmd>) {
+ return write(out, cmd);
+}
+
+inline std::tuple<ReadStream, bool> read(ReadStream in, Command cmd) {
+ volatile const Command* pos = reinterpret_cast<volatile const Command*>(in.pos_);
+ in += sizeof(Command);
+ if (!in) return {in, false};
+ return {in, *pos == cmd};
+}
+
+template <Command cmd>
+std::tuple<ReadStream, bool> read(Message<Cmd<cmd>>, ReadStream in) {
+ return read(in, cmd);
+}
+
+inline WriteStream write(Message<>, WriteStream out) {
+ return out;
+}
+
+template <typename Head, typename... Tail>
+WriteStream write(Message<Head, Tail...>, WriteStream out, const Head& head, const Tail&... tail) {
+ out = write(out, head);
+ return write(Message<Tail...>(), out, tail...);
+}
+
+template <Command cmd, typename... Tail>
+WriteStream write(Message<Cmd<cmd>, Tail...>, WriteStream out, const Tail&... tail) {
+ out = write(out, cmd);
+ return write(Message<Tail...>(), out, tail...);
+}
+
+template <Command cmd, typename HEAD, typename... Tail>
+std::tuple<ReadStream, bool, HEAD, Tail...> read(Message<Cmd<cmd>, HEAD, Tail...>, ReadStream in) {
+ bool command_matches;
+ std::tie(in, command_matches) = read(in, cmd);
+ if (!command_matches) return {in, false, HEAD(), Tail()...};
+
+ return {in, true,
+ [&]() -> HEAD {
+ HEAD result;
+ std::tie(in, result) = read(Message<HEAD>(), in);
+ return result;
+ }(),
+ [&]() -> Tail {
+ Tail result;
+ std::tie(in, result) = read(Message<Tail>(), in);
+ return result;
+ }()...};
+}
+
+template <typename... Msg>
+std::tuple<ReadStream, Msg...> read(Message<Msg...>, ReadStream in) {
+ return {in, [&in]() -> Msg {
+ Msg result;
+ std::tie(in, result) = read(Message<Msg>(), in);
+ return result;
+ }()...};
+}
+
+template <typename T>
+struct msg2tuple {};
+
+template <typename... T>
+struct msg2tuple<Message<T...>> {
+ using type = std::tuple<T...>;
+};
+template <Command cmd, typename... T>
+struct msg2tuple<Message<Cmd<cmd>, T...>> {
+ using type = std::tuple<T...>;
+};
+
+template <typename T>
+using msg2tuple_t = typename msg2tuple<T>::type;
+
+template <size_t... idx, typename HEAD, typename... T>
+std::tuple<T&&...> tuple_tail(IntegerSequence<idx...>, std::tuple<HEAD, T...>&& t) {
+ return {std::move(std::get<idx>(t))...};
+}
+
+template <size_t... idx, typename HEAD, typename... T>
+std::tuple<const T&...> tuple_tail(IntegerSequence<idx...>, const std::tuple<HEAD, T...>& t) {
+ return {std::get<idx>(t)...};
+}
+
+template <typename HEAD, typename... Tail>
+std::tuple<Tail&&...> tuple_tail(std::tuple<HEAD, Tail...>&& t) {
+ return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), std::move(t));
+}
+
+template <typename HEAD, typename... Tail>
+std::tuple<const Tail&...> tuple_tail(const std::tuple<HEAD, Tail...>& t) {
+ return tuple_tail(integer_sequence::make_t<1, sizeof...(Tail)>(), t);
+}
+
+} // namespace support
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
+
+#endif // CONFIRMATIONUI_SUPPORT_INCLUDE_ANDROID_HARDWARE_CONFIRMATIONUI_SUPPORT_MSG_FORMATTING_H_
diff --git a/confirmationui/support/src/cbor.cpp b/confirmationui/support/src/cbor.cpp
new file mode 100644
index 0000000..e7ea164
--- /dev/null
+++ b/confirmationui/support/src/cbor.cpp
@@ -0,0 +1,111 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <android/hardware/confirmationui/support/cbor.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace support {
+namespace {
+
+inline uint8_t getByte(const uint64_t& v, const uint8_t index) {
+ return v >> (index * 8);
+}
+
+WriteState writeBytes(WriteState state, uint64_t value, uint8_t size) {
+ auto pos = state.data_;
+ if (!(state += size)) return state;
+ switch (size) {
+ case 8:
+ *pos++ = getByte(value, 7);
+ *pos++ = getByte(value, 6);
+ *pos++ = getByte(value, 5);
+ *pos++ = getByte(value, 4);
+ case 4:
+ *pos++ = getByte(value, 3);
+ *pos++ = getByte(value, 2);
+ case 2:
+ *pos++ = getByte(value, 1);
+ case 1:
+ *pos++ = value;
+ break;
+ default:
+ state.error_ = Error::MALFORMED;
+ }
+ return state;
+}
+
+} // anonymous namespace
+
+WriteState writeHeader(WriteState wState, Type type, const uint64_t value) {
+ if (!wState) return wState;
+ uint8_t& header = *wState.data_;
+ if (!++wState) return wState;
+ header = static_cast<uint8_t>(type) << 5;
+ if (value < 24) {
+ header |= static_cast<uint8_t>(value);
+ } else if (value < 0x100) {
+ header |= 24;
+ wState = writeBytes(wState, value, 1);
+ } else if (value < 0x10000) {
+ header |= 25;
+ wState = writeBytes(wState, value, 2);
+ } else if (value < 0x100000000) {
+ header |= 26;
+ wState = writeBytes(wState, value, 4);
+ } else {
+ header |= 27;
+ wState = writeBytes(wState, value, 8);
+ }
+ return wState;
+}
+
+bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out) {
+ uint32_t multi_byte_length = 0;
+ while (begin != end) {
+ if (multi_byte_length) {
+ // parsing multi byte character - must start with 10xxxxxx
+ --multi_byte_length;
+ if ((*begin & 0xc0) != 0x80) return false;
+ } else if (!((*begin) & 0x80)) {
+ // 7bit character -> nothing to be done
+ } else {
+ // msb is set and we were not parsing a multi byte character
+ // so this must be a header byte
+ char c = *begin << 1;
+ while (c & 0x80) {
+ ++multi_byte_length;
+ c <<= 1;
+ }
+ // headers of the form 10xxxxxx are not allowed
+ if (multi_byte_length < 1) return false;
+ // chars longer than 4 bytes are not allowed (multi_byte_length does not count the
+ // header thus > 3
+ if (multi_byte_length > 3) return false;
+ }
+ if (out) *out++ = *reinterpret_cast<const uint8_t*>(begin++);
+ }
+ // if the string ends in the middle of a multi byte char it is invalid
+ if (multi_byte_length) return false;
+ return true;
+}
+
+} // namespace support
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/confirmationui/support/src/confirmationui_utils.cpp b/confirmationui/support/src/confirmationui_utils.cpp
new file mode 100644
index 0000000..708f0d56
--- /dev/null
+++ b/confirmationui/support/src/confirmationui_utils.cpp
@@ -0,0 +1,39 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <android/hardware/confirmationui/support/confirmationui_utils.h>
+
+namespace android {
+namespace hardware {
+namespace confirmationui {
+namespace support {
+
+bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs) {
+ if (lhs.size() == rhs.size()) {
+ auto lhsi = lhs.begin();
+ auto rhsi = rhs.begin();
+ while (lhsi != lhs.end()) {
+ if (*lhsi++ != *rhsi++) return false;
+ }
+ }
+ return true;
+}
+
+} // namespace support
+} // namespace confirmationui
+} // namespace hardware
+} // namespace android
diff --git a/confirmationui/support/test/android_cbor_test.cpp b/confirmationui/support/test/android_cbor_test.cpp
new file mode 100644
index 0000000..4a5a362
--- /dev/null
+++ b/confirmationui/support/test/android_cbor_test.cpp
@@ -0,0 +1,197 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <android/hardware/confirmationui/support/cbor.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <iomanip>
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+using namespace android::hardware::confirmationui::support;
+
+uint8_t testVector[] = {
+ 0xA4, 0x63, 0x6B, 0x65, 0x79, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x63, 0x6B, 0x65, 0x79, 0x4D,
+ 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x31, 0x30, 0x00, 0x04, 0x07, 0x1B,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x07, 0x1A, 0xFD, 0x49, 0x8C, 0xFF,
+ 0xFF, 0x82, 0x69, 0xE2, 0x99, 0xA8, 0xE2, 0x9A, 0x96, 0xE2, 0xB6, 0x96, 0x59, 0x01, 0x91, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00,
+};
+
+// 400 'a's and a '\0'
+constexpr char fourHundredAs[] =
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaa";
+
+WriteState writeTest(WriteState state) {
+ return write(state, //
+ map( //
+ pair(text("key"), text("value")), //
+ pair(text("key"), bytes("100101010010")), //
+ pair(4, 7), //
+ pair((UINT64_C(1) << 62), INT64_C(-2000000000000000)) //
+ ), //
+ arr(text("♨⚖ⶖ"), bytes(fourHundredAs)));
+}
+
+TEST(Cbor, FeatureTest) {
+ uint8_t buffer[0x1000];
+ WriteState state(buffer);
+ state = writeTest(state);
+ ASSERT_EQ(sizeof(testVector), size_t(state.data_ - buffer));
+ ASSERT_EQ(Error::OK, state.error_);
+ ASSERT_EQ(0, memcmp(buffer, testVector, sizeof(testVector)));
+}
+
+// Test if in all write cases an out of data error is correctly propagated and we don't
+// write beyond the end of the buffer.
+TEST(Cbor, BufferTooShort) {
+ uint8_t buffer[0x1000];
+ for (size_t s = 1; s < sizeof(testVector); ++s) {
+ memset(buffer, 0x22, 0x1000); // 0x22 is not in the testVector
+ WriteState state(buffer, s);
+ state = writeTest(state);
+ for (size_t t = s; t < 0x1000; ++t) {
+ ASSERT_EQ(0x22, buffer[t]); // check if a canary has been killed
+ }
+ ASSERT_EQ(Error::OUT_OF_DATA, state.error_);
+ }
+}
+
+TEST(Cbor, MalformedUTF8Test_Stray) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char malformed[] = {char(0x80), 0};
+ state = write(state, text(malformed));
+ ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
+}
+
+TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char malformed[] = {char(0xc0), 0};
+ state = write(state, text(malformed));
+ ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
+}
+
+TEST(Cbor, UTF8Test_TwoBytes) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char neat[] = {char(0xc3), char(0x82), 0};
+ state = write(state, text(neat));
+ ASSERT_EQ(Error::OK, state.error_);
+}
+
+TEST(Cbor, UTF8Test_ThreeBytes) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char neat[] = {char(0xe3), char(0x82), char(0x82), 0};
+ state = write(state, text(neat));
+ ASSERT_EQ(Error::OK, state.error_);
+}
+
+TEST(Cbor, UTF8Test_FourBytes) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char neat[] = {char(0xf3), char(0x82), char(0x82), char(0x82), 0};
+ state = write(state, text(neat));
+ ASSERT_EQ(Error::OK, state.error_);
+}
+
+TEST(Cbor, MalformendUTF8Test_CharacterTooLong) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char malformed[] = {char(0xf8), char(0x82), char(0x82), char(0x82), char(0x82), 0};
+ state = write(state, text(malformed));
+ ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
+}
+
+TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte2) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ char malformed[] = {char(0xc0), char(0x82), char(0x83), 0};
+ state = write(state, text(malformed));
+ ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
+}
+
+TEST(Cbor, MinimalViableHeaderSizeTest) {
+ uint8_t buffer[20];
+ WriteState state(buffer);
+ state = writeHeader(state, Type::NUMBER, 23);
+ ASSERT_EQ(state.data_ - buffer, 1);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 24);
+ ASSERT_EQ(state.data_ - buffer, 2);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0xff);
+ ASSERT_EQ(state.data_ - buffer, 2);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0x100);
+ ASSERT_EQ(state.data_ - buffer, 3);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0xffff);
+ ASSERT_EQ(state.data_ - buffer, 3);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0x10000);
+ ASSERT_EQ(state.data_ - buffer, 5);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0xffffffff);
+ ASSERT_EQ(state.data_ - buffer, 5);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0x100000000);
+ ASSERT_EQ(state.data_ - buffer, 9);
+
+ state = WriteState(buffer);
+ state = writeHeader(state, Type::NUMBER, 0xffffffffffffffff);
+ ASSERT_EQ(state.data_ - buffer, 9);
+}
diff --git a/confirmationui/support/test/gtest_main.cpp b/confirmationui/support/test/gtest_main.cpp
new file mode 100644
index 0000000..43fc5a3
--- /dev/null
+++ b/confirmationui/support/test/gtest_main.cpp
@@ -0,0 +1,23 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <gtest/gtest.h>
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/confirmationui/support/test/msg_formatting_test.cpp b/confirmationui/support/test/msg_formatting_test.cpp
new file mode 100644
index 0000000..90ed84c
--- /dev/null
+++ b/confirmationui/support/test/msg_formatting_test.cpp
@@ -0,0 +1,128 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stddef.h>
+#include <stdint.h>
+#include <iomanip>
+#include <iostream>
+#include <string>
+
+#include <android/hardware/confirmationui/support/msg_formatting.h>
+#include <gtest/gtest.h>
+
+using android::hardware::confirmationui::support::Message;
+using android::hardware::confirmationui::support::WriteStream;
+using android::hardware::confirmationui::support::ReadStream;
+using android::hardware::confirmationui::support::PromptUserConfirmationMsg;
+using android::hardware::confirmationui::support::write;
+using ::android::hardware::confirmationui::V1_0::UIOption;
+using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
+using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+#ifdef DEBUG_MSG_FORMATTING
+namespace {
+
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+std::ostream& hexdump(std::ostream& out, const uint8_t* data, size_t size) {
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t byte = data[i];
+ out << (nibble2hex[0x0F & (byte >> 4)]);
+ out << (nibble2hex[0x0F & byte]);
+ switch (i & 0xf) {
+ case 0xf:
+ out << "\n";
+ break;
+ case 7:
+ out << " ";
+ break;
+ default:
+ out << " ";
+ break;
+ }
+ }
+ return out;
+}
+
+} // namespace
+#endif
+
+TEST(MsgFormattingTest, FeatureTest) {
+ uint8_t buffer[0x1000];
+
+ WriteStream out(buffer);
+ out = unalign(out);
+ out += 4;
+ auto begin = out.pos();
+ out = write(
+ PromptUserConfirmationMsg(), out, hidl_string("Do you?"),
+ hidl_vec<uint8_t>{0x01, 0x02, 0x03}, hidl_string("en"),
+ hidl_vec<UIOption>{UIOption::AccessibilityInverted, UIOption::AccessibilityMagnified});
+
+ ReadStream in(buffer);
+ in = unalign(in);
+ in += 4;
+ hidl_string prompt;
+ hidl_vec<uint8_t> extra;
+ hidl_string locale;
+ hidl_vec<UIOption> uiOpts;
+ bool command_matches;
+ std::tie(in, command_matches, prompt, extra, locale, uiOpts) =
+ read(PromptUserConfirmationMsg(), in);
+ ASSERT_TRUE(in);
+ ASSERT_TRUE(command_matches);
+ ASSERT_EQ(hidl_string("Do you?"), prompt);
+ ASSERT_EQ((hidl_vec<uint8_t>{0x01, 0x02, 0x03}), extra);
+ ASSERT_EQ(hidl_string("en"), locale);
+ ASSERT_EQ(
+ (hidl_vec<UIOption>{UIOption::AccessibilityInverted, UIOption::AccessibilityMagnified}),
+ uiOpts);
+
+#ifdef DEBUG_MSG_FORMATTING
+ hexdump(std::cout, buffer, 100) << std::endl;
+#endif
+
+ // The following assertions check that the hidl_[vec|string] types are in fact read in place,
+ // and no copying occurs. Copying results in heap allocation which we intend to avoid.
+ ASSERT_EQ(8, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(prompt.c_str())) - begin);
+ ASSERT_EQ(20, extra.data() - begin);
+ ASSERT_EQ(27, const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(locale.c_str())) - begin);
+ ASSERT_EQ(40, reinterpret_cast<uint8_t*>(uiOpts.data()) - begin);
+}
+
+TEST(MsgFormattingTest, HardwareAuthTokenTest) {
+ uint8_t buffer[0x1000];
+
+ HardwareAuthToken expected, actual;
+ expected.authenticatorId = 0xa1a3a4a5a6a7a8;
+ expected.authenticatorType = HardwareAuthenticatorType::NONE;
+ expected.challenge = 0xb1b2b3b4b5b6b7b8;
+ expected.userId = 0x1122334455667788;
+ expected.timestamp = 0xf1f2f3f4f5f6f7f8;
+ expected.mac =
+ hidl_vec<uint8_t>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
+
+ WriteStream out(buffer);
+ out = write(Message<HardwareAuthToken>(), out, expected);
+ ReadStream in(buffer);
+ std::tie(in, actual) = read(Message<HardwareAuthToken>(), in);
+ ASSERT_EQ(expected, actual);
+}
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index 1695ef7..809f694 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -93,6 +93,7 @@
requestType = KeyRequestType::RELEASE;
break;
case android::DrmPlugin::kKeyRequestType_Unknown:
+ default:
requestType = KeyRequestType::UNKNOWN;
break;
}
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
index c895af6..ed8196e 100644
--- a/drm/1.1/Android.bp
+++ b/drm/1.1/Android.bp
@@ -18,6 +18,7 @@
],
types: [
"HdcpLevel",
+ "KeyRequestType",
"SecurityLevel",
],
gen_java: false,
diff --git a/drm/1.1/IDrmPlugin.hal b/drm/1.1/IDrmPlugin.hal
index 0660a43..7dd397a 100644
--- a/drm/1.1/IDrmPlugin.hal
+++ b/drm/1.1/IDrmPlugin.hal
@@ -17,8 +17,14 @@
import @1.0::IDrmPlugin;
import @1.0::IDrmPluginListener;
+import @1.0::KeyedVector;
+import @1.0::KeyType;
import @1.0::Status;
+import @1.1::DrmMetricGroup;
import @1.1::HdcpLevel;
+import @1.1::KeyRequestType;
+import @1.0::SecureStopId;
+import @1.1::SecureStopRelease;
import @1.1::SecurityLevel;
/**
@@ -28,6 +34,59 @@
*/
interface IDrmPlugin extends @1.0::IDrmPlugin {
/**
+ * A key request/response exchange occurs between the app and a License
+ * Server to obtain the keys required to decrypt the content.
+ * getKeyRequest_1_1() is used to obtain an opaque key request blob that is
+ * delivered to the license server.
+ *
+ * getKeyRequest_1_1() only differs from getKeyRequest() in that additional
+ * values are returned in 1.1::KeyRequestType as compared to
+ * 1.0::KeyRequestType
+ *
+ * @param scope may be a sessionId or a keySetId, depending on the
+ * specified keyType. When the keyType is OFFLINE or STREAMING,
+ * scope should be set to the sessionId the keys will be provided
+ * to. When the keyType is RELEASE, scope should be set to the
+ * keySetId of the keys being released.
+ * @param initData container-specific data, its meaning is interpreted
+ * based on the mime type provided in the mimeType parameter.
+ * It could contain, for example, the content ID, key ID or
+ * other data obtained from the content metadata that is
+ * required to generate the key request. initData may be empty
+ * when keyType is RELEASE.
+ * @param mimeType identifies the mime type of the content
+ * @param keyType specifies if the keys are to be used for streaming,
+ * offline or a release
+ * @param optionalParameters included in the key request message to
+ * allow a client application to provide additional message
+ * parameters to the server.
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: ERROR_DRM_SESSION_NOT_OPENED if the
+ * session is not opened, ERROR_DRM_NOT_PROVISIONED if the device
+ * requires provisioning before it can generate a key request,
+ * ERROR_DRM_CANNOT_HANDLE if getKeyRequest is not supported
+ * at the time of the call, BAD_VALUE if any parameters are
+ * invalid or ERROR_DRM_INVALID_STATE if the HAL is in a
+ * state where a key request cannot be generated.
+ * @return request if successful, the opaque key request blob is returned
+ * @return requestType indicates type information about the returned
+ * request. The type may be one of INITIAL, RENEWAL, RELEASE,
+ * NONE or UPDATE. An INITIAL request is the first key request
+ * for a license. RENEWAL is a subsequent key request used to
+ * refresh the keys in a license. RELEASE corresponds to a
+ * keyType of RELEASE, which indicates keys are being released.
+ * NONE indicates that no request is needed because the keys are
+ * already loaded. UPDATE indicates that the keys need to be
+ * refetched after the initial license request.
+ * @return defaultUrl the URL that the request may be sent to, if
+ * provided by the drm HAL. The app may choose to override this URL.
+ */
+ getKeyRequest_1_1(vec<uint8_t> scope, vec<uint8_t> initData,
+ string mimeType, KeyType keyType, KeyedVector optionalParameters)
+ generates (Status status, vec<uint8_t> request,
+ KeyRequestType requestType, string defaultUrl);
+
+ /**
* Return the currently negotiated and max supported HDCP levels.
*
* The current level is based on the display(s) the device is connected to.
@@ -106,4 +165,69 @@
*/
setSecurityLevel(vec<uint8_t> sessionId, SecurityLevel level)
generates(Status status);
+
+ /**
+ * Returns the plugin-specific metrics. Multiple metric groups may be
+ * returned in one call to getMetrics(). The scope and definition of the
+ * metrics is defined by the plugin.
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the metrics are not available to be
+ * returned.
+ * @return metric_groups the collection of metric groups provided by the
+ * plugin.
+ */
+ getMetrics() generates (Status status, vec<DrmMetricGroup> metric_groups);
+
+ /**
+ * Get the IDs of all secure stops on the device
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * IDs cannot be returned.
+ * @return secureStopIds a list of the IDs
+ */
+ getSecureStopIds() generates
+ (Status status, vec<SecureStopId> secureStopIds);
+
+ /**
+ * Release secure stops given a release message from the key server
+ *
+ * @param ssRelease the secure stop release message identifying one or more
+ * secure stops to release. ssRelease is opaque, it is passed directly from
+ * a DRM license server through the app and media framework to the vendor
+ * HAL module. The format and content of ssRelease must be defined by the
+ * DRM scheme being implemented according to this HAL. The DRM scheme
+ * can be identified by its UUID which can be queried using
+ * IDrmFactory::isCryptoSchemeSupported.
+ *
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: BAD_VALUE if ssRelease is invalid or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * cannot be released.
+ */
+ releaseSecureStops(SecureStopRelease ssRelease) generates (Status status);
+
+ /**
+ * Remove a secure stop given its secure stop ID, without requiring
+ * a secure stop release response message from the key server.
+ *
+ * @param secureStopId the ID of the secure stop to release.
+ *
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: BAD_VALUE if the secureStopId is invalid or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop
+ * cannot be released.
+ */
+ removeSecureStop(SecureStopId secureStopId) generates (Status status);
+
+ /**
+ * Remove all secure stops on the device without requiring a secure
+ * stop release response message from the key server.
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure
+ * stops cannot be removed.
+ */
+ removeAllSecureStops() generates (Status status);
};
diff --git a/drm/1.1/types.hal b/drm/1.1/types.hal
index 9447524..015f1b7 100644
--- a/drm/1.1/types.hal
+++ b/drm/1.1/types.hal
@@ -16,6 +16,105 @@
package android.hardware.drm@1.1;
+import @1.0::KeyRequestType;
+
+/**
+ * This message contains plugin-specific metrics made available to the client.
+ * The message is used for making vendor-specific metrics available to an
+ * application. The framework is not consuming any of the information.
+ *
+ * Metrics are grouped in instances of DrmMetricGroup. Each group contains
+ * multiple instances of Metric.
+ *
+ * Example:
+ *
+ * Capture the timing information of a buffer copy event, "buf_copy", broken
+ * out by the "size" of the buffer.
+ *
+ * DrmMetricGroup {
+ * metrics[0] {
+ * name: "buf_copy"
+ * attributes[0] {
+ * name: "size"
+ * type: INT64_TYPE
+ * int64Value: 1024
+ * }
+ * values[0] {
+ * componentName: "operation_count"
+ * type: INT64_TYPE
+ * int64Value: 75
+ * }
+ * values[1] {
+ * component_name: "average_time_seconds"
+ * type: DOUBLE_TYPE
+ * doubleValue: 0.00000042
+ * }
+ * }
+ * }
+ */
+struct DrmMetricGroup {
+ /**
+ * Used to discriminate the type of value being stored in the structs
+ * below.
+ */
+ enum ValueType : uint8_t {
+ INT64_TYPE,
+ DOUBLE_TYPE,
+ STRING_TYPE,
+ };
+
+ /**
+ * A detail about the metric being captured. The fields of an Attribute
+ * are opaque to the framework.
+ */
+ struct Attribute {
+ string name;
+ /**
+ * The type field indicates which of the following values is used.
+ */
+ ValueType type;
+ int64_t int64Value;
+ double doubleValue;
+ string stringValue;
+ };
+
+ /**
+ * A value of the metric. A metric may have multiple values. The
+ * component name may be left empty if there is only supposed to be
+ * one value for the given metric. The fields of the Value are
+ * opaque to the framework.
+ */
+ struct Value {
+ string componentName;
+ /**
+ * The type field indicates which of the following values is used.
+ */
+ ValueType type;
+ int64_t int64Value;
+ double doubleValue;
+ string stringValue;
+ };
+
+ /**
+ * The metric being captured. A metric must have a name and at least one
+ * value. A metric may have 0 or more attributes. The fields of a Metric
+ * are opaque to the framework.
+ */
+ struct Metric {
+ string name;
+ vec<Attribute> attributes;
+ // A Metric may have one or more values. Multiple values are useful
+ // for capturing different aspects of the same metric. E.g. capture
+ // the min, max, average, count, and stdev of a particular metric.
+ vec<Value> values;
+ };
+
+ /**
+ * The list of metrics to be captured.
+ */
+ vec<Metric> metrics;
+};
+
/**
* HDCP specifications are defined by Digital Content Protection LLC (DCP).
* "HDCP Specification Rev. 2.2 Interface Independent Adaptation"
@@ -58,6 +157,23 @@
HDCP_NO_OUTPUT
};
+/**
+ * KeyRequestTypes (in addition to those from 1.0) which allow an app
+ * to determine the type of a key request returned from getKeyRequest.
+ */
+enum KeyRequestType : @1.0::KeyRequestType {
+ /**
+ * Keys are already loaded. No key request is needed.
+ */
+ NONE,
+
+ /**
+ * Keys have previously been loaded. An additional (non-renewal) license
+ * request is needed.
+ */
+ UPDATE,
+};
+
enum SecurityLevel : uint32_t {
/**
* Unable to determine the security level
@@ -93,3 +209,11 @@
*/
HW_SECURE_ALL,
};
+
+/**
+ * Encapsulates a secure stop release opaque object
+ */
+struct SecureStopRelease {
+ vec<uint8_t> opaqueData;
+};
+
diff --git a/gnss/1.1/IGnss.hal b/gnss/1.1/IGnss.hal
index 0c3d876..096f251 100644
--- a/gnss/1.1/IGnss.hal
+++ b/gnss/1.1/IGnss.hal
@@ -17,6 +17,7 @@
package android.hardware.gnss@1.1;
import @1.0::IGnss;
+import @1.0::GnssLocation;
import IGnssCallback;
import IGnssConfiguration;
@@ -78,4 +79,16 @@
* @return gnssMeasurementIface Handle to the IGnssMeasurement interface.
*/
getExtensionGnssMeasurement_1_1() generates (IGnssMeasurement gnssMeasurementIface);
+
+ /**
+ * Injects current location from the best available location provider.
+ *
+ * Unlike injectLocation, this method may inject a recent GNSS location from the HAL
+ * implementation, if that is the best available location known to the framework.
+ *
+ * @param location Location information from the best available location provider.
+ *
+ * @return success Returns true if successful.
+ */
+ injectBestLocation(GnssLocation location) generates (bool success);
};
\ No newline at end of file
diff --git a/gnss/1.1/IGnssCallback.hal b/gnss/1.1/IGnssCallback.hal
index 7a2849e..9fd71ae 100644
--- a/gnss/1.1/IGnssCallback.hal
+++ b/gnss/1.1/IGnssCallback.hal
@@ -35,4 +35,17 @@
* @param name String providing the name of the GNSS HAL implementation
*/
gnssNameCb(string name);
+
+ /**
+ * Callback for requesting Location.
+ *
+ * HAL implementation shall call this when it wants the framework to provide location to assist
+ * with GNSS HAL operation. For example, to assist with time to first fix, and/or error
+ * recovery, it may ask for a location that is independent from GNSS (e.g. from the "network"
+ * LocationProvier), or to provide a Device-Based-Hybrid location to supplement A-GPS/GNSS
+ * emergency call flows managed by the GNSS HAL.
+ *
+ * @param independentFromGnss True if requesting a location that is independent from GNSS.
+ */
+ gnssRequestLocationCb(bool independentFromGnss);
};
\ No newline at end of file
diff --git a/gnss/1.1/vts/functional/gnss_hal_test.h b/gnss/1.1/vts/functional/gnss_hal_test.h
index a06db5d..6aab3cb 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test.h
+++ b/gnss/1.1/vts/functional/gnss_hal_test.h
@@ -74,6 +74,9 @@
}
Return<void> gnssAcquireWakelockCb() override { return Void(); }
Return<void> gnssReleaseWakelockCb() override { return Void(); }
+ Return<void> gnssRequestLocationCb(bool /* independentFromGnss */) override {
+ return Void();
+ }
Return<void> gnssRequestTimeCb() override { return Void(); }
// Actual (test) callback handlers
Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
diff --git a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
index c9e36a9..b4bad2c 100644
--- a/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/1.1/vts/functional/gnss_hal_test_cases.cpp
@@ -23,6 +23,8 @@
using android::hardware::hidl_vec;
using android::hardware::gnss::V1_0::GnssConstellationType;
+using android::hardware::gnss::V1_0::GnssLocation;
+using android::hardware::gnss::V1_0::IGnssDebug;
using android::hardware::gnss::V1_1::IGnssConfiguration;
using android::hardware::gnss::V1_1::IGnssMeasurement;
@@ -363,4 +365,83 @@
result = gnss_configuration_hal->setBlacklist(sources);
ASSERT_TRUE(result.isOk());
EXPECT_TRUE(result);
-}
\ No newline at end of file
+}
+
+/*
+ * InjectBestLocation
+ *
+ * Ensure successfully injecting a location.
+ */
+TEST_F(GnssHalTest, InjectBestLocation) {
+ GnssLocation gnssLocation = {.gnssLocationFlags = 0, // set below
+ .latitudeDegrees = 43.0,
+ .longitudeDegrees = -180,
+ .altitudeMeters = 1000,
+ .speedMetersPerSec = 0,
+ .bearingDegrees = 0,
+ .horizontalAccuracyMeters = 0.1,
+ .verticalAccuracyMeters = 0.1,
+ .speedAccuracyMetersPerSecond = 0.1,
+ .bearingAccuracyDegrees = 0.1,
+ .timestamp = 1534567890123L};
+ gnssLocation.gnssLocationFlags |=
+ GnssLocationFlags::HAS_LAT_LONG | GnssLocationFlags::HAS_ALTITUDE |
+ GnssLocationFlags::HAS_SPEED | GnssLocationFlags::HAS_HORIZONTAL_ACCURACY |
+ GnssLocationFlags::HAS_VERTICAL_ACCURACY | GnssLocationFlags::HAS_SPEED_ACCURACY |
+ GnssLocationFlags::HAS_BEARING | GnssLocationFlags::HAS_BEARING_ACCURACY;
+
+ CheckLocation(gnssLocation, true);
+
+ auto result = gnss_hal_->injectBestLocation(gnssLocation);
+
+ ASSERT_TRUE(result.isOk());
+ EXPECT_TRUE(result);
+}
+
+/*
+ * GnssDebugValuesSanityTest:
+ * Ensures that GnssDebug values make sense.
+ */
+TEST_F(GnssHalTest, GnssDebugValuesSanityTest) {
+ auto gnssDebug = gnss_hal_->getExtensionGnssDebug();
+ ASSERT_TRUE(gnssDebug.isOk());
+ if (info_called_count_ > 0 && last_info_.yearOfHw >= 2017) {
+ sp<IGnssDebug> iGnssDebug = gnssDebug;
+ EXPECT_NE(iGnssDebug, nullptr);
+
+ IGnssDebug::DebugData data;
+ iGnssDebug->getDebugData(
+ [&data](const IGnssDebug::DebugData& debugData) { data = debugData; });
+
+ if (data.position.valid) {
+ EXPECT_GE(data.position.latitudeDegrees, -90);
+ EXPECT_LE(data.position.latitudeDegrees, 90);
+
+ EXPECT_GE(data.position.longitudeDegrees, -180);
+ EXPECT_LE(data.position.longitudeDegrees, 180);
+
+ EXPECT_GE(data.position.altitudeMeters, -1000); // Dead Sea: -414m
+ EXPECT_LE(data.position.altitudeMeters, 20000); // Mount Everest: 8850m
+
+ EXPECT_GE(data.position.speedMetersPerSec, 0);
+ EXPECT_LE(data.position.speedMetersPerSec, 600);
+
+ EXPECT_GE(data.position.bearingDegrees, -360);
+ EXPECT_LE(data.position.bearingDegrees, 360);
+
+ EXPECT_GE(data.position.horizontalAccuracyMeters, 0);
+ EXPECT_LE(data.position.horizontalAccuracyMeters, 20000000);
+
+ EXPECT_GE(data.position.verticalAccuracyMeters, 0);
+ EXPECT_LE(data.position.verticalAccuracyMeters, 20000);
+
+ EXPECT_GE(data.position.speedAccuracyMetersPerSecond, 0);
+ EXPECT_LE(data.position.speedAccuracyMetersPerSecond, 500);
+
+ EXPECT_GE(data.position.bearingAccuracyDegrees, 0);
+ EXPECT_LE(data.position.bearingAccuracyDegrees, 180);
+
+ EXPECT_GE(data.position.ageSeconds, 0);
+ }
+ }
+}
diff --git a/graphics/common/1.1/Android.bp b/graphics/common/1.1/Android.bp
index 72ef282..c319d80 100644
--- a/graphics/common/1.1/Android.bp
+++ b/graphics/common/1.1/Android.bp
@@ -15,6 +15,7 @@
],
types: [
"BufferUsage",
+ "Dataspace",
"PixelFormat",
],
gen_java: true,
diff --git a/graphics/common/1.1/types.hal b/graphics/common/1.1/types.hal
index 135f6e3..b917d5e 100644
--- a/graphics/common/1.1/types.hal
+++ b/graphics/common/1.1/types.hal
@@ -18,6 +18,7 @@
import @1.0::PixelFormat;
import @1.0::BufferUsage;
+import @1.0::Dataspace;
/**
* Pixel formats for graphics buffers.
@@ -77,6 +78,39 @@
* defined by the dataspace.
*/
STENCIL_8 = 0x35,
+
+ /**
+ * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed immediately by a Wx(H/2) CbCr plane. Each sample is
+ * represented by a 16-bit little-endian value, with the lower 6 bits set
+ * to zero.
+ *
+ * This format assumes
+ * - an even height
+ * - a vertical stride equal to the height
+ *
+ * stride_in_bytes = stride * 2
+ * y_size = stride_in_bytes * height
+ * cbcr_size = stride_in_bytes * (height / 2)
+ * cb_offset = y_size
+ * cr_offset = cb_offset + 2
+ *
+ * This format must be accepted by the allocator when used with the
+ * following usage flags:
+ *
+ * - BufferUsage::VIDEO_*
+ * - BufferUsage::CPU_*
+ * - BufferUsage::GPU_TEXTURE
+ *
+ * The component values are unsigned normalized to the range [0, 1], whose
+ * interpretation is defined by the dataspace.
+ *
+ * This format is appropriate for 10bit video content.
+ *
+ * Buffers with this format must be locked with IMapper::lockYCbCr
+ * or with IMapper::lock.
+ */
+ YCBCR_P010 = 0x36,
};
/**
@@ -91,3 +125,19 @@
/** bits 27 and 32-47 must be zero and are reserved for future versions */
};
+
+@export(name="android_dataspace_v1_1_t", value_prefix="HAL_DATASPACE_",
+ export_parent="false")
+enum Dataspace : @1.0::Dataspace {
+ /**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ * limited range is the preferred / normative definition for BT.2020
+ */
+ BT2020_ITU = STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_LIMITED,
+
+ BT2020_ITU_PQ = STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED,
+};
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
index 29b9de3..00d9d8c 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerTestUtils.h
@@ -58,10 +58,12 @@
std::string dumpDebugInfo();
std::unique_ptr<ComposerClient> createClient();
+ protected:
+ sp<IComposer> mComposer;
+
private:
void init();
- sp<IComposer> mComposer;
std::unordered_set<IComposer::Capability> mCapabilities;
};
diff --git a/graphics/composer/2.2/Android.bp b/graphics/composer/2.2/Android.bp
new file mode 100644
index 0000000..633a208
--- /dev/null
+++ b/graphics/composer/2.2/Android.bp
@@ -0,0 +1,20 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.graphics.composer@2.2",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "IComposer.hal",
+ "IComposerClient.hal",
+ ],
+ interfaces: [
+ "android.hardware.graphics.common@1.0",
+ "android.hardware.graphics.composer@2.1",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+}
+
diff --git a/graphics/composer/2.2/IComposer.hal b/graphics/composer/2.2/IComposer.hal
new file mode 100644
index 0000000..05052c8
--- /dev/null
+++ b/graphics/composer/2.2/IComposer.hal
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.2;
+
+import @2.1::IComposer;
+
+interface IComposer extends @2.1::IComposer {
+
+ /* createClient from @2.1::IComposer must return an instance of @2.2::IComposerClient */
+
+};
diff --git a/graphics/composer/2.2/IComposerClient.hal b/graphics/composer/2.2/IComposerClient.hal
new file mode 100644
index 0000000..dcd9c8d
--- /dev/null
+++ b/graphics/composer/2.2/IComposerClient.hal
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.2;
+
+import android.hardware.graphics.common@1.0::PixelFormat;
+import android.hardware.graphics.common@1.0::Dataspace;
+import @2.1::IComposerClient;
+import @2.1::Display;
+import @2.1::Error;
+
+interface IComposerClient extends @2.1::IComposerClient {
+
+ enum PowerMode : @2.1::IComposerClient.PowerMode {
+ /**
+ * The display is configured as in ON but may stop applying display
+ * updates from the client. This is effectively a hint to the device
+ * that drawing to the display has been suspended and that the the
+ * device must remain on and continue displaying its current contents
+ * indefinitely until the power mode changes.
+ *
+ * This mode may also be used as a signal to enable hardware-based
+ * functionality to take over the display and manage it autonomously
+ * to implement a low power always-on display.
+ */
+ ON_SUSPEND = 4
+ };
+
+ /**
+ * Following enums define keys for metadata defined by SMPTE ST 2086:2014
+ * and CTA 861.3.
+ */
+ enum PerFrameMetadataKey : int32_t {
+ /** SMPTE ST 2084:2014.
+ * Coordinates defined in CIE 1931 xy chromaticity space
+ */
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_RED_PRIMARY_X,
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_RED_PRIMARY_Y,
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_GREEN_PRIMARY_X,
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_GREEN_PRIMARY_Y,
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_BLUE_PRIMARY_X,
+ /** SMPTE ST 2084:2014 */
+ DISPLAY_BLUE_PRIMARY_Y,
+ /** SMPTE ST 2084:2014 */
+ WHITE_POINT_X,
+ /** SMPTE ST 2084:2014 */
+ WHITE_POINT_Y,
+ /** SMPTE ST 2084:2014.
+ * Units: nits
+ * max as defined by ST 2048: 10,000 nits
+ */
+ MAX_LUMINANCE,
+ /** SMPTE ST 2084:2014 */
+ MIN_LUMINANCE,
+ /** CTA 861.3 */
+ MAX_CONTENT_LIGHT_LEVEL,
+ /** CTA 861.3 */
+ MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ };
+
+ struct PerFrameMetadata {
+ PerFrameMetadataKey key;
+ float value;
+ };
+
+ struct FloatColor {
+ float r;
+ float g;
+ float b;
+ float a;
+ };
+
+ enum Command : @2.1::IComposerClient.Command {
+ /**
+ * setPerFrameMetadata(Display display, vec<PerFrameMetadata> data)
+ * Sets the PerFrameMetadata for the display. This metadata must be used
+ * by the implementation to better tone map content to that display.
+ *
+ * This is a method that may be called every frame. Thus it's
+ * implemented using buffered transport.
+ * SET_PER_FRAME_METADATA is the command used by the buffered transport
+ * mechanism.
+ */
+ SET_PER_FRAME_METADATA = 0x207 << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+
+ /**
+ * SET_LAYER_COLOR has this pseudo prototype
+ *
+ * setLayerColor(FloatColor color);
+ *
+ * Sets the color of the given layer. If the composition type of the layer
+ * is not Composition::SOLID_COLOR, this call must succeed and have no
+ * other effect.
+ *
+ * @param color is the new color using float type.
+ */
+ SET_LAYER_FLOAT_COLOR = 0x40c << @2.1::IComposerClient.Command:OPCODE_SHIFT,
+ };
+
+ /**
+ * Returns the PerFrameMetadataKeys that are supported by this device.
+ *
+ * @param display is the display on which to create the layer.
+ * @return keys is the vector of PerFrameMetadataKey keys that are
+ * supported by this device.
+ * @return error is NONE upon success. Otherwise,
+ * UNSUPPORTED if not supported on underlying HAL
+ */
+ getPerFrameMetadataKeys(Display display)
+ generates (Error error,
+ vec<PerFrameMetadataKey> keys);
+
+ /**
+ * getReadbackBufferAttributes
+ * Returns the format which should be used when allocating a buffer for use by
+ * device readback as well as the dataspace in which its contents should be
+ * interpreted.
+ *
+ * The width and height of this buffer must be those of the currently-active
+ * display configuration, and the usage flags must consist of the following:
+ * BufferUsage::CPU_READ | BufferUsage::GPU_TEXTURE |
+ * BufferUsage::COMPOSER_OUTPUT
+ *
+ * The format and dataspace provided must be sufficient such that if a
+ * correctly-configured buffer is passed into setReadbackBuffer, filled by
+ * the device, and then displayed by the client as a full-screen buffer, the
+ * output of the display remains the same (subject to the note about protected
+ * content in the description of setReadbackBuffer).
+ *
+ * Parameters:
+ * @param display - the display on which to create the layer.
+ *
+ * @return format - the format the client should use when allocating a device
+ * readback buffer
+ * @return dataspace - the dataspace to use when interpreting the
+ * contents of a device readback buffer
+ * @return error is NONE upon success. Otherwise,
+ * BAD_DISPLAY when an invalid display handle was passed in.
+ * UNSUPPORTED if not supported on underlying HAL
+ *
+ * See also:
+ * setReadbackBuffer
+ * getReadbackBufferFence
+ */
+ getReadbackBufferAttributes(Display display)
+ generates (Error error,
+ PixelFormat format,
+ Dataspace dataspace);
+
+ /**
+ * getReadbackBufferFence
+ * Returns an acquire sync fence file descriptor which must signal when the
+ * buffer provided to setReadbackBuffer has been filled by the device and is
+ * safe for the client to read.
+ *
+ * If it is already safe to read from this buffer, -1 may be returned instead.
+ * The client takes ownership of this file descriptor and is responsible for
+ * closing it when it is no longer needed.
+ *
+ * This function must be called immediately after the composition cycle being
+ * captured into the readback buffer. The complete ordering of a readback buffer
+ * capture is as follows:
+ *
+ * getReadbackBufferAttributes
+ * // Readback buffer is allocated
+ * // Many frames may pass
+ *
+ * setReadbackBuffer
+ * validateDisplay
+ * presentDisplay
+ * getReadbackBufferFence
+ * // Implicitly wait on the acquire fence before accessing the buffer
+ *
+ * Parameters:
+ * @param display - the display on which to create the layer.
+ *
+ * @return acquireFence - a sync fence file descriptor as described above; pointer
+ * must be non-NULL
+ * @return error - is HWC2_ERROR_NONE or one of the following errors:
+ * BAD_DISPLAY - an invalid display handle was passed in
+ * UNSUPPORTED if not supported on underlying HAL
+ *
+ * See also:
+ * getReadbackBufferAttributes
+ * setReadbackBuffer
+ */
+ getReadbackBufferFence(Display display)
+ generates (Error error,
+ handle acquireFence);
+
+ /**
+ * setReadbackBuffer
+ * Sets the readback buffer to be filled with the contents of the next
+ * composition performed for this display (i.e., the contents present at the
+ * time of the next validateDisplay/presentDisplay cycle).
+ *
+ * This buffer must have been allocated as described in
+ * getReadbackBufferAttributes and is in the dataspace provided by the same.
+ *
+ * If there is hardware protected content on the display at the time of the next
+ * composition, the area of the readback buffer covered by such content must be
+ * completely black. Any areas of the buffer not covered by such content may
+ * optionally be black as well.
+ *
+ * The release fence file descriptor provided works identically to the one
+ * described for setOutputBuffer.
+ *
+ * This function must not be called between any call to validateDisplay and a
+ * subsequent call to presentDisplay.
+ *
+ * Parameters:
+ * @param display - the display on which to create the layer.
+ * @param buffer - the new readback buffer
+ * @param releaseFence - a sync fence file descriptor as described in setOutputBuffer
+ *
+ * @return error - is HWC2_ERROR_NONE or one of the following errors:
+ * HWC2_ERROR_BAD_DISPLAY - an invalid display handle was passed in
+ * HWC2_ERROR_BAD_PARAMETER - the new readback buffer handle was invalid
+ *
+ * See also:
+ * getReadbackBufferAttributes
+ * getReadbackBufferFence
+ */
+ setReadbackBuffer(Display display, handle buffer, handle releaseFence) generates (Error error);
+
+ /**
+ * setPowerMode_2_2
+ * Sets the power mode of the given display. The transition must be
+ * complete when this function returns. It is valid to call this function
+ * multiple times with the same power mode.
+ *
+ * All displays must support PowerMode::ON and PowerMode::OFF. Whether a
+ * display supports PowerMode::DOZE or PowerMode::DOZE_SUSPEND may be
+ * queried using getDozeSupport.
+ *
+ * @param display is the display to which the power mode is set.
+ * @param mode is the new power mode.
+ * @return error is NONE upon success. Otherwise,
+ * BAD_DISPLAY when an invalid display handle was passed in.
+ * BAD_PARAMETER when mode was not a valid power mode.
+ * UNSUPPORTED when mode is not supported on this display.
+ */
+ setPowerMode_2_2(Display display, PowerMode mode) generates (Error error);
+
+};
diff --git a/graphics/composer/2.2/utils/OWNERS b/graphics/composer/2.2/utils/OWNERS
new file mode 100644
index 0000000..1beb074
--- /dev/null
+++ b/graphics/composer/2.2/utils/OWNERS
@@ -0,0 +1,8 @@
+# Graphics team
+courtneygo@google.com
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.2/utils/command-buffer/Android.bp b/graphics/composer/2.2/utils/command-buffer/Android.bp
new file mode 100644
index 0000000..efaabd4
--- /dev/null
+++ b/graphics/composer/2.2/utils/command-buffer/Android.bp
@@ -0,0 +1,13 @@
+cc_library_headers {
+ name: "android.hardware.graphics.composer@2.2-command-buffer",
+ defaults: ["hidl_defaults"],
+ vendor_available: true,
+ shared_libs: [
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ ],
+ header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ ],
+ export_include_dirs: ["include"],
+}
diff --git a/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h b/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h
new file mode 100644
index 0000000..c803d3c
--- /dev/null
+++ b/graphics/composer/2.2/utils/command-buffer/include/composer-command-buffer/2.2/ComposerCommandBuffer.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef LOG_TAG
+#warn "ComposerCommandBuffer.h included without LOG_TAG"
+#endif
+
+#undef LOG_NDEBUG
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <android/hardware/graphics/composer/2.2/IComposer.h>
+#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
+#include <fmq/MessageQueue.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+
+using android::hardware::MessageQueue;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Transform;
+using android::hardware::graphics::composer::V2_1::Config;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::IComposerCallback;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+
+using CommandQueueType = MessageQueue<uint32_t, kSynchronizedReadWrite>;
+
+// This class helps build a command queue. Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandWriterBase : public V2_1::CommandWriterBase {
+ public:
+ CommandWriterBase(uint32_t initialMaxSize) : V2_1::CommandWriterBase(initialMaxSize) {}
+
+ static constexpr uint16_t kSetLayerFloatColorLength = 4;
+ void setLayerFloatColor(IComposerClient::FloatColor color) {
+ beginCommand_2_2(IComposerClient::Command::SET_LAYER_FLOAT_COLOR,
+ kSetLayerFloatColorLength);
+ writeFloatColor(color);
+ endCommand();
+ }
+
+ void setPerFrameMetadata(const hidl_vec<IComposerClient::PerFrameMetadata>& metadataVec) {
+ beginCommand_2_2(IComposerClient::Command::SET_PER_FRAME_METADATA, metadataVec.size() * 2);
+ for (const auto& metadata : metadataVec) {
+ writeSigned(static_cast<int32_t>(metadata.key));
+ writeFloat(metadata.value);
+ }
+ endCommand();
+ }
+
+ protected:
+ void beginCommand_2_2(IComposerClient::Command command, uint16_t length) {
+ V2_1::CommandWriterBase::beginCommand(
+ static_cast<V2_1::IComposerClient::Command>(static_cast<int32_t>(command)), length);
+ }
+
+ void writeFloatColor(const IComposerClient::FloatColor& color) {
+ writeFloat(color.r);
+ writeFloat(color.g);
+ writeFloat(color.b);
+ writeFloat(color.a);
+ }
+};
+
+// This class helps parse a command queue. Note that all sizes/lengths are in
+// units of uint32_t's.
+class CommandReaderBase : public V2_1::CommandReaderBase {
+ public:
+ CommandReaderBase() : V2_1::CommandReaderBase(){};
+
+ protected:
+ IComposerClient::FloatColor readFloatColor() {
+ float r = readFloat();
+ float g = readFloat();
+ float b = readFloat();
+ float a = readFloat();
+ return IComposerClient::FloatColor{r, g, b, a};
+ }
+};
+
+} // namespace V2_2
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
new file mode 100644
index 0000000..0325a6c
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "libVtsHalGraphicsComposerTestUtils@2.2",
+ defaults: ["hidl_defaults"],
+ srcs: [
+ "VtsHalGraphicsComposerTestUtils.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ "libfmq",
+ "libsync",
+ ],
+ static_libs: [
+ "libVtsHalGraphicsComposerTestUtils",
+ "VtsHalHidlTargetTestBase",
+ ],
+ header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ "android.hardware.graphics.composer@2.2-command-buffer",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-O0",
+ "-g",
+ "-DLOG_TAG=\"GraphicsComposerTestUtils@2.2\"",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "VtsHalGraphicsComposerV2_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalGraphicsComposerV2_2TargetTest.cpp"],
+
+ // TODO(b/64437680): Assume these libs are always available on the device.
+ shared_libs: [
+ "libfmq",
+ "libhidltransport",
+ "libsync",
+ ],
+ static_libs: [
+ "android.hardware.graphics.allocator@2.0",
+ "android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.mapper@2.0",
+ "android.hardware.graphics.mapper@2.1",
+ "libVtsHalGraphicsComposerTestUtils",
+ "libVtsHalGraphicsComposerTestUtils@2.2",
+ "libVtsHalGraphicsMapperTestUtils",
+ "libnativehelper",
+ ],
+ header_libs: [
+ "android.hardware.graphics.composer@2.1-command-buffer",
+ "android.hardware.graphics.composer@2.2-command-buffer",
+ ],
+}
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
new file mode 100644
index 0000000..1beb074
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -0,0 +1,8 @@
+# Graphics team
+courtneygo@google.com
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
+zhuoyao@google.com
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp
new file mode 100644
index 0000000..00946ce
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerTestUtils.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <hidl/HidlTransportUtils.h>
+
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include "2.2/VtsHalGraphicsComposerTestUtils.h"
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::details::getDescriptor;
+using android::hardware::details::canCastInterface;
+
+std::unique_ptr<ComposerClient_v2_2> Composer_v2_2::createClient_v2_2() {
+ std::unique_ptr<ComposerClient_v2_2> client;
+ mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to create client";
+ ALOGV("tmpClient is a %s", getDescriptor(&(*tmpClient)).c_str());
+ ASSERT_TRUE(canCastInterface(
+ &(*tmpClient), "android.hardware.graphics.composer@2.2::IComposerClient", false))
+ << "Cannot create 2.2 IComposerClient";
+ client = std::make_unique<ComposerClient_v2_2>(IComposerClient::castFrom(tmpClient, true));
+ });
+
+ return client;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> ComposerClient_v2_2::getPerFrameMetadataKeys(
+ Display display) {
+ std::vector<IComposerClient::PerFrameMetadataKey> keys;
+ mClient_v2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to get HDR metadata keys";
+ keys = tmpKeys;
+ });
+
+ return keys;
+}
+
+void ComposerClient_v2_2::execute_v2_2(V2_1::tests::TestCommandReader* reader,
+ V2_2::CommandWriterBase* writer) {
+ bool queueChanged = false;
+ uint32_t commandLength = 0;
+ hidl_vec<hidl_handle> commandHandles;
+ ASSERT_TRUE(writer->writeQueue(&queueChanged, &commandLength, &commandHandles));
+
+ if (queueChanged) {
+ auto ret = mClient_v2_2->setInputCommandQueue(*writer->getMQDescriptor());
+ ASSERT_EQ(Error::NONE, static_cast<Error>(ret));
+ return;
+ }
+
+ mClient_v2_2->executeCommands(commandLength, commandHandles,
+ [&](const auto& tmpError, const auto& tmpOutQueueChanged,
+ const auto& tmpOutLength, const auto& tmpOutHandles) {
+ ASSERT_EQ(Error::NONE, tmpError);
+
+ if (tmpOutQueueChanged) {
+ mClient_v2_2->getOutputCommandQueue(
+ [&](const auto& tmpError, const auto& tmpDescriptor) {
+ ASSERT_EQ(Error::NONE, tmpError);
+ reader->setMQDescriptor(tmpDescriptor);
+ });
+ }
+
+ ASSERT_TRUE(reader->readQueue(tmpOutLength, tmpOutHandles));
+ reader->parse();
+ });
+}
+
+void ComposerClient_v2_2::setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) {
+ Error error = mClient_v2_2->setPowerMode_2_2(display, mode);
+ ASSERT_TRUE(error == Error::NONE || error == Error::UNSUPPORTED) << "failed to set power mode";
+}
+
+void ComposerClient_v2_2::setReadbackBuffer(Display display, const native_handle_t* buffer,
+ int32_t /* releaseFence */) {
+ // Ignoring fence, HIDL doesn't care
+ Error error = mClient_v2_2->setReadbackBuffer(display, buffer, nullptr);
+ ASSERT_EQ(Error::NONE, error) << "failed to setReadbackBuffer";
+}
+
+void ComposerClient_v2_2::getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
+ Dataspace* outDataspace) {
+ mClient_v2_2->getReadbackBufferAttributes(
+ display,
+ [&](const auto& tmpError, const auto& tmpOutPixelFormat, const auto& tmpOutDataspace) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback buffer attributes";
+ *outPixelFormat = tmpOutPixelFormat;
+ *outDataspace = tmpOutDataspace;
+ });
+}
+
+void ComposerClient_v2_2::getReadbackBufferFence(Display display, int32_t* outFence) {
+ hidl_handle handle;
+ mClient_v2_2->getReadbackBufferFence(display, [&](const auto& tmpError, const auto& tmpHandle) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to get readback fence";
+ handle = tmpHandle;
+ });
+ *outFence = 0;
+}
+
+} // namespace tests
+} // namespace V2_2
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
new file mode 100644
index 0000000..8b44d61
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "graphics_composer_hidl_hal_test@2.2"
+
+#include <android-base/logging.h>
+#include <android/hardware/graphics/mapper/2.1/IMapper.h>
+#include <sync/sync.h>
+#include "2.2/VtsHalGraphicsComposerTestUtils.h"
+#include "GraphicsComposerCallback.h"
+#include "TestCommandReader.h"
+#include "VtsHalGraphicsComposerTestUtils.h"
+#include "VtsHalGraphicsMapperTestUtils.h"
+
+#include <VtsHalHidlTargetTestBase.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+namespace {
+
+using android::hardware::graphics::common::V1_0::BufferUsage;
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::ColorTransform;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_0::Transform;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+using android::hardware::graphics::mapper::V2_0::IMapper;
+using android::hardware::graphics::mapper::V2_0::tests::Gralloc;
+using GrallocError = android::hardware::graphics::mapper::V2_0::Error;
+
+// Test environment for graphics.composer
+class GraphicsComposerHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static GraphicsComposerHidlEnvironment* Instance() {
+ static GraphicsComposerHidlEnvironment* instance = new GraphicsComposerHidlEnvironment;
+ return instance;
+ }
+
+ virtual void registerTestServices() override { registerTestService<IComposer>(); }
+
+ private:
+ GraphicsComposerHidlEnvironment() {}
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(GraphicsComposerHidlEnvironment);
+};
+
+class GraphicsComposerHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ protected:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(
+ mComposer = std::make_unique<Composer_v2_2>(
+ GraphicsComposerHidlEnvironment::Instance()->getServiceName<IComposer>()));
+ ASSERT_NO_FATAL_FAILURE(mComposerClient = mComposer->createClient_v2_2());
+
+ mComposerCallback = new V2_1::tests::GraphicsComposerCallback;
+ mComposerClient->registerCallback(mComposerCallback);
+
+ // assume the first display is primary and is never removed
+ mPrimaryDisplay = waitForFirstDisplay();
+
+ // explicitly disable vsync
+ mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+ mComposerCallback->setVsyncAllowed(false);
+ }
+
+ void TearDown() override {
+ if (mComposerCallback != nullptr) {
+ EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
+ EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
+ EXPECT_EQ(0, mComposerCallback->getInvalidVsyncCount());
+ }
+ }
+
+ // use the slot count usually set by SF
+ static constexpr uint32_t kBufferSlotCount = 64;
+
+ std::unique_ptr<Composer_v2_2> mComposer;
+ std::unique_ptr<ComposerClient_v2_2> mComposerClient;
+ sp<V2_1::tests::GraphicsComposerCallback> mComposerCallback;
+ // the first display and is assumed never to be removed
+ Display mPrimaryDisplay;
+
+ private:
+ Display waitForFirstDisplay() {
+ while (true) {
+ std::vector<Display> displays = mComposerCallback->getDisplays();
+ if (displays.empty()) {
+ usleep(5 * 1000);
+ continue;
+ }
+
+ return displays[0];
+ }
+ }
+};
+
+// Tests for IComposerClient::Command.
+class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
+ protected:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
+
+ ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
+
+ mWriter = std::make_unique<V2_2::CommandWriterBase>(1024);
+ mReader = std::make_unique<V2_1::tests::TestCommandReader>();
+ }
+
+ void TearDown() override { ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown()); }
+
+ const native_handle_t* allocate() {
+ IMapper::BufferDescriptorInfo info{};
+ info.width = 64;
+ info.height = 64;
+ info.layerCount = 1;
+ info.format = PixelFormat::RGBA_8888;
+ info.usage =
+ static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN);
+
+ return mGralloc->allocate(info);
+ }
+
+ void execute() { mComposerClient->execute_v2_2(mReader.get(), mWriter.get()); }
+
+ std::unique_ptr<V2_2::CommandWriterBase> mWriter;
+ std::unique_ptr<V2_1::tests::TestCommandReader> mReader;
+
+ private:
+ std::unique_ptr<Gralloc> mGralloc;
+};
+
+/**
+ * Test IComposerClient::Command::SET_PER_FRAME_METADATA.
+ */
+TEST_F(GraphicsComposerHidlCommandTest, SET_PER_FRAME_METADATA) {
+ Layer layer;
+ ASSERT_NO_FATAL_FAILURE(layer =
+ mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+
+ /**
+ * DISPLAY_P3 is a color space that uses the DCI_P3 primaries,
+ * the D65 white point and the SRGB transfer functions.
+ * Rendering Intent: Colorimetric
+ * Primaries:
+ * x y
+ * green 0.265 0.690
+ * blue 0.150 0.060
+ * red 0.680 0.320
+ * white (D65) 0.3127 0.3290
+ */
+
+ std::vector<IComposerClient::PerFrameMetadata> hidlMetadata;
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, 0.680});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, 0.320});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, 0.265});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, 0.690});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, 0.150});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, 0.060});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::WHITE_POINT_X, 0.3127});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::WHITE_POINT_Y, 0.3290});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MAX_LUMINANCE, 100.0});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MIN_LUMINANCE, 0.1});
+ hidlMetadata.push_back({IComposerClient::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, 78.0});
+ hidlMetadata.push_back(
+ {IComposerClient::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, 62.0});
+ mWriter->setPerFrameMetadata(hidlMetadata);
+ execute();
+}
+
+/**
+ * Test IComposerClient::getPerFrameMetadataKeys.
+ */
+TEST_F(GraphicsComposerHidlTest, GetPerFrameMetadataKeys) {
+ mComposerClient->getPerFrameMetadataKeys(mPrimaryDisplay);
+}
+/**
+ * Test IComposerClient::setPowerMode_2_2.
+ */
+TEST_F(GraphicsComposerHidlTest, setPowerMode_2_2) {
+ std::vector<IComposerClient::PowerMode> modes;
+ modes.push_back(IComposerClient::PowerMode::OFF);
+ modes.push_back(IComposerClient::PowerMode::ON_SUSPEND);
+ modes.push_back(IComposerClient::PowerMode::ON);
+
+ for (auto mode : modes) {
+ mComposerClient->setPowerMode_2_2(mPrimaryDisplay, mode);
+ }
+}
+
+TEST_F(GraphicsComposerHidlTest, setReadbackBuffer) {
+ mComposerClient->setReadbackBuffer(mPrimaryDisplay, nullptr, -1);
+}
+
+TEST_F(GraphicsComposerHidlTest, getReadbackBufferFence) {
+ int32_t fence;
+ mComposerClient->getReadbackBufferFence(mPrimaryDisplay, &fence);
+}
+
+TEST_F(GraphicsComposerHidlTest, getReadbackBufferAttributes) {
+ PixelFormat pixelFormat;
+ Dataspace dataspace;
+ mComposerClient->getReadbackBufferAttributes(mPrimaryDisplay, &pixelFormat, &dataspace);
+}
+
+/**
+ * Test IComposerClient::Command::SET_LAYER_FLOAT_COLOR.
+ */
+TEST_F(GraphicsComposerHidlCommandTest, SET_LAYER_FLOAT_COLOR) {
+ V2_1::Layer layer;
+ ASSERT_NO_FATAL_FAILURE(layer =
+ mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+
+ mWriter->selectDisplay(mPrimaryDisplay);
+ mWriter->selectLayer(layer);
+ mWriter->setLayerFloatColor(IComposerClient::FloatColor{1.0, 1.0, 1.0, 1.0});
+ mWriter->setLayerFloatColor(IComposerClient::FloatColor{0.0, 0.0, 0.0, 0.0});
+}
+
+} // namespace
+} // namespace tests
+} // namespace V2_2
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
+
+int main(int argc, char** argv) {
+ using android::hardware::graphics::composer::V2_2::tests::GraphicsComposerHidlEnvironment;
+ ::testing::AddGlobalTestEnvironment(GraphicsComposerHidlEnvironment::Instance());
+ ::testing::InitGoogleTest(&argc, argv);
+ GraphicsComposerHidlEnvironment::Instance()->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ return status;
+}
diff --git a/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h b/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h
new file mode 100644
index 0000000..c5756ed
--- /dev/null
+++ b/graphics/composer/2.2/vts/functional/include/2.2/VtsHalGraphicsComposerTestUtils.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <VtsHalGraphicsComposerTestUtils.h>
+#include <VtsHalHidlTargetTestBase.h>
+#include <android/hardware/graphics/composer/2.2/IComposer.h>
+#include <android/hardware/graphics/composer/2.2/IComposerClient.h>
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace hardware {
+namespace graphics {
+namespace composer {
+namespace V2_2 {
+namespace tests {
+
+using android::hardware::graphics::common::V1_0::ColorMode;
+using android::hardware::graphics::common::V1_0::Dataspace;
+using android::hardware::graphics::common::V1_0::Hdr;
+using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::composer::V2_2::IComposer;
+using android::hardware::graphics::composer::V2_2::IComposerClient;
+
+class ComposerClient_v2_2;
+
+// Only thing I need for Composer_v2_2 is to create a v2_2 ComposerClient
+// Everything else is the same
+class Composer_v2_2 : public V2_1::tests::Composer {
+ public:
+ Composer_v2_2() : V2_1::tests::Composer(){};
+ explicit Composer_v2_2(const std::string& name) : V2_1::tests::Composer(name){};
+
+ std::unique_ptr<ComposerClient_v2_2> createClient_v2_2();
+};
+
+// A wrapper to IComposerClient.
+class ComposerClient_v2_2
+ : public android::hardware::graphics::composer::V2_1::tests::ComposerClient {
+ public:
+ ComposerClient_v2_2(const sp<IComposerClient>& client)
+ : V2_1::tests::ComposerClient(client), mClient_v2_2(client){};
+
+ void execute_v2_2(V2_1::tests::TestCommandReader* reader, V2_2::CommandWriterBase* writer);
+
+ std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(Display display);
+
+ void setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode);
+ void setReadbackBuffer(Display display, const native_handle_t* buffer, int32_t releaseFence);
+ void getReadbackBufferAttributes(Display display, PixelFormat* outPixelFormat,
+ Dataspace* outDataspace);
+ void getReadbackBufferFence(Display display, int32_t* outFence);
+
+ private:
+ sp<V2_2::IComposerClient> mClient_v2_2;
+};
+
+} // namespace tests
+} // namespace V2_2
+} // namespace composer
+} // namespace graphics
+} // namespace hardware
+} // namespace android
diff --git a/graphics/mapper/2.1/IMapper.hal b/graphics/mapper/2.1/IMapper.hal
index a23656d..6047e96 100644
--- a/graphics/mapper/2.1/IMapper.hal
+++ b/graphics/mapper/2.1/IMapper.hal
@@ -16,10 +16,47 @@
package android.hardware.graphics.mapper@2.1;
-import android.hardware.graphics.mapper@2.0::Error;
-import android.hardware.graphics.mapper@2.0::IMapper;
+import android.hardware.graphics.common@1.1::BufferUsage;
+import android.hardware.graphics.common@1.1::PixelFormat;
+import @2.0::BufferDescriptor;
+import @2.0::Error;
+import @2.0::IMapper;
-interface IMapper extends android.hardware.graphics.mapper@2.0::IMapper {
+interface IMapper extends @2.0::IMapper {
+ /**
+ * This is the same as @2.0::IMapper::BufferDescriptorInfo except that it
+ * accepts @1.1::PixelFormat and @1.1::BufferUsage.
+ */
+ struct BufferDescriptorInfo {
+ /**
+ * The width specifies how many columns of pixels must be in the
+ * allocated buffer, but does not necessarily represent the offset in
+ * columns between the same column in adjacent rows. The rows may be
+ * padded.
+ */
+ uint32_t width;
+
+ /**
+ * The height specifies how many rows of pixels must be in the
+ * allocated buffer.
+ */
+ uint32_t height;
+
+ /**
+ * The number of image layers that must be in the allocated buffer.
+ */
+ uint32_t layerCount;
+
+ /** Buffer pixel format. */
+ PixelFormat format;
+
+ /**
+ * Buffer usage mask; valid flags can be found in the definition of
+ * BufferUsage.
+ */
+ bitfield<BufferUsage> usage;
+ };
+
/**
* Validate that the buffer can be safely accessed by a caller who assumes
* the specified descriptorInfo and stride. This must at least validate
@@ -58,4 +95,30 @@
generates (Error error,
uint32_t numFds,
uint32_t numInts);
+
+ /**
+ * This is the same as @2.0::IMapper::createDescriptor except that it
+ * accepts @2.1::IMapper::BufferDescriptorInfo.
+ *
+ * Creates a buffer descriptor. The descriptor can be used with IAllocator
+ * to allocate buffers.
+ *
+ * Since the buffer descriptor fully describes a buffer, any device
+ * dependent or device independent checks must be performed here whenever
+ * possible. Specifically, when layered buffers are not supported, this
+ * function must return UNSUPPORTED if layerCount is great than 1.
+ *
+ * @param descriptorInfo specifies the attributes of the descriptor.
+ * @return error is NONE upon success. Otherwise,
+ * BAD_VALUE when any of the specified attributes is
+ * invalid or conflicting.
+ * NO_RESOURCES when the creation cannot be fullfilled at
+ * this time.
+ * UNSUPPORTED when any of the specified attributes is
+ * not supported.
+ * @return descriptor is the newly created buffer descriptor.
+ */
+ createDescriptor_2_1(BufferDescriptorInfo descriptorInfo)
+ generates (Error error,
+ BufferDescriptor descriptor);
};
diff --git a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
index 4067c8d..88b96ae 100644
--- a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
+++ b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
@@ -30,10 +30,27 @@
namespace tests {
namespace {
+using android::hardware::graphics::mapper::V2_0::BufferDescriptor;
using android::hardware::graphics::mapper::V2_0::Error;
-using android::hardware::graphics::common::V1_0::BufferUsage;
-using android::hardware::graphics::common::V1_0::PixelFormat;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+using android::hardware::graphics::common::V1_1::PixelFormat;
+
+// abuse VTS to check binary compatibility between BufferDescriptorInfos
+using OldBufferDescriptorInfo =
+ android::hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo;
+static_assert(sizeof(OldBufferDescriptorInfo) == sizeof(IMapper::BufferDescriptorInfo) &&
+ offsetof(OldBufferDescriptorInfo, width) ==
+ offsetof(IMapper::BufferDescriptorInfo, width) &&
+ offsetof(OldBufferDescriptorInfo, height) ==
+ offsetof(IMapper::BufferDescriptorInfo, height) &&
+ offsetof(OldBufferDescriptorInfo, layerCount) ==
+ offsetof(IMapper::BufferDescriptorInfo, layerCount) &&
+ offsetof(OldBufferDescriptorInfo, format) ==
+ offsetof(IMapper::BufferDescriptorInfo, format) &&
+ offsetof(OldBufferDescriptorInfo, usage) ==
+ offsetof(IMapper::BufferDescriptorInfo, usage),
+ "");
class Gralloc : public V2_0::tests::Gralloc {
public:
@@ -72,6 +89,32 @@
});
}
+ BufferDescriptor createDescriptor(const IMapper::BufferDescriptorInfo& descriptorInfo) {
+ BufferDescriptor descriptor;
+ mMapper->createDescriptor_2_1(
+ descriptorInfo, [&](const auto& tmpError, const auto& tmpDescriptor) {
+ ASSERT_EQ(Error::NONE, tmpError) << "failed to create descriptor";
+ descriptor = tmpDescriptor;
+ });
+
+ return descriptor;
+ }
+
+ const native_handle_t* allocate(const IMapper::BufferDescriptorInfo& descriptorInfo,
+ bool import, uint32_t* outStride = nullptr) {
+ BufferDescriptor descriptor = createDescriptor(descriptorInfo);
+ if (::testing::Test::HasFatalFailure()) {
+ return nullptr;
+ }
+
+ auto buffers = V2_0::tests::Gralloc::allocate(descriptor, 1, import, outStride);
+ if (::testing::Test::HasFatalFailure()) {
+ return nullptr;
+ }
+
+ return buffers[0];
+ }
+
private:
void init() {
mMapper = IMapper::castFrom(V2_0::tests::Gralloc::getMapper());
@@ -229,6 +272,24 @@
native_handle_delete(rawBufferHandle);
}
+/**
+ * Test IMapper::createDescriptor with valid descriptor info.
+ */
+TEST_F(GraphicsMapperHidlTest, CreateDescriptor_2_1Basic) {
+ ASSERT_NO_FATAL_FAILURE(mGralloc->createDescriptor(mDummyDescriptorInfo));
+}
+
+/**
+ * Test IMapper::createDescriptor with invalid descriptor info.
+ */
+TEST_F(GraphicsMapperHidlTest, CreateDescriptor_2_1Negative) {
+ auto info = mDummyDescriptorInfo;
+ info.width = 0;
+ mGralloc->getMapper()->createDescriptor_2_1(info, [&](const auto& tmpError, const auto&) {
+ EXPECT_EQ(Error::BAD_VALUE, tmpError) << "createDescriptor did not fail with BAD_VALUE";
+ });
+}
+
} // namespace
} // namespace tests
} // namespace V2_1
@@ -238,7 +299,10 @@
} // namespace android
int main(int argc, char** argv) {
+ using android::hardware::graphics::mapper::V2_0::tests::GraphicsMapperHidlEnvironment;
+ ::testing::AddGlobalTestEnvironment(GraphicsMapperHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);
+ GraphicsMapperHidlEnvironment::Instance()->init(&argc, argv);
int status = RUN_ALL_TESTS();
LOG(INFO) << "Test result = " << status;
diff --git a/health/2.0/Android.bp b/health/2.0/Android.bp
index c444165..0325467 100644
--- a/health/2.0/Android.bp
+++ b/health/2.0/Android.bp
@@ -16,9 +16,9 @@
"android.hidl.base@1.0",
],
types: [
- "Result",
"DiskStats",
"HealthInfo",
+ "Result",
"StorageAttribute",
"StorageInfo",
],
diff --git a/keymaster/4.0/Android.bp b/keymaster/4.0/Android.bp
index 20c40a0..2daad41 100644
--- a/keymaster/4.0/Android.bp
+++ b/keymaster/4.0/Android.bp
@@ -15,16 +15,26 @@
"android.hidl.base@1.0",
],
types: [
+ "Algorithm",
+ "BlockMode",
"Constants",
+ "Digest",
+ "EcCurve",
"ErrorCode",
"HardwareAuthToken",
+ "HardwareAuthenticatorType",
"HmacSharingParameters",
+ "KeyBlobUsageRequirements",
"KeyCharacteristics",
+ "KeyDerivationFunction",
+ "KeyFormat",
"KeyOrigin",
"KeyParameter",
"KeyPurpose",
+ "PaddingMode",
"SecurityLevel",
"Tag",
+ "TagType",
"VerificationToken",
],
gen_java: false,
diff --git a/keymaster/4.0/IKeymasterDevice.hal b/keymaster/4.0/IKeymasterDevice.hal
index 5dac929..aef81c7 100644
--- a/keymaster/4.0/IKeymasterDevice.hal
+++ b/keymaster/4.0/IKeymasterDevice.hal
@@ -374,10 +374,27 @@
/**
* Upgrades an old key blob. Keys can become "old" in two ways: Keymaster can be upgraded to a
* new version with an incompatible key blob format, or the system can be updated to invalidate
- * the OS version and/or patch level. In either case, attempts to use an old key blob with
- * getKeyCharacteristics(), exportKey(), attestKey() or begin() must result in Keymaster
- * returning ErrorCode::KEY_REQUIRES_UPGRADE. The caller must use this method to upgrade the
- * key blob.
+ * the OS version (OS_VERSION tag), system patch level (OS_PATCHLEVEL tag), vendor patch level
+ * (VENDOR_PATCH_LEVEL tag), boot patch level (BOOT_PATCH_LEVEL tag) or other,
+ * implementation-defined patch level (keymaster implementers are encouraged to extend this HAL
+ * with a minor version extension to define validatable patch levels for other images; tags
+ * must be defined in the implemeter's namespace, starting at 10000). In either case,
+ * attempts to use an old key blob with getKeyCharacteristics(), exportKey(), attestKey() or
+ * begin() must result in Keymaster returning ErrorCode::KEY_REQUIRES_UPGRADE. The caller must
+ * use this method to upgrade the key blob.
+ *
+ * If upgradeKey is asked to update a key with any version or patch level that is higher than
+ * the current system version or patch level, it must return ErrorCode::INVALID_ARGUMENT. There
+ * is one exception: it is always permissible to "upgrade" from any OS_VERSION number to
+ * OS_VERSION 0. For example, if the key has OS_VERSION 080001, it is permisible to upgrade the
+ * key if the current system version is 080100, because the new version is larger, or if the
+ * current system version is 0, because upgrades to 0 are always allowed. If the system version
+ * were 080000, however, keymaster must return ErrorCode::INVALID_ARGUMENT because that value is
+ * smaller than 080001.
+ *
+ * Note that Keymaster versions 2 and 3 required that the system and boot images have the same
+ * patch level and OS version. This requirement is relaxed for Keymaster 4, and the OS version
+ * in the boot image footer is no longer used.
*
* @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey();
*
diff --git a/keymaster/4.0/support/Android.bp b/keymaster/4.0/support/Android.bp
index 6b8dcdc..ccd1b56 100644
--- a/keymaster/4.0/support/Android.bp
+++ b/keymaster/4.0/support/Android.bp
@@ -26,6 +26,8 @@
"attestation_record.cpp",
"authorization_set.cpp",
"key_param_output.cpp",
+ "keymaster_utils.cpp",
+ "Keymaster.cpp",
"Keymaster3.cpp",
"Keymaster4.cpp",
],
diff --git a/keymaster/4.0/support/Keymaster.cpp b/keymaster/4.0/support/Keymaster.cpp
new file mode 100644
index 0000000..bf52c47
--- /dev/null
+++ b/keymaster/4.0/support/Keymaster.cpp
@@ -0,0 +1,90 @@
+/*
+ ** 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.
+ */
+
+#include <keymasterV4_0/Keymaster.h>
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <keymasterV4_0/Keymaster3.h>
+#include <keymasterV4_0/Keymaster4.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+using ::android::sp;
+using ::android::hidl::manager::V1_0::IServiceManager;
+
+template <typename Wrapper>
+std::vector<std::unique_ptr<Keymaster>> enumerateDevices(
+ const sp<IServiceManager>& serviceManager) {
+ std::vector<std::unique_ptr<Keymaster>> result;
+
+ bool foundDefault = false;
+ auto& descriptor = Wrapper::WrappedIKeymasterDevice::descriptor;
+ serviceManager->listByInterface(descriptor, [&](const hidl_vec<hidl_string>& names) {
+ for (auto& name : names) {
+ if (name == "default") foundDefault = true;
+ auto device = Wrapper::WrappedIKeymasterDevice::getService();
+ CHECK(device) << "Failed to get service for " << descriptor << " with interface name "
+ << name;
+ result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, name)));
+ }
+ });
+
+ if (!foundDefault) {
+ // "default" wasn't provided by listByInterface. Maybe there's a passthrough
+ // implementation.
+ auto device = Wrapper::WrappedIKeymasterDevice::getService("default");
+ if (device) result.push_back(std::unique_ptr<Keymaster>(new Wrapper(device, "default")));
+ }
+
+ return result;
+}
+
+std::vector<std::unique_ptr<Keymaster>> Keymaster::enumerateAvailableDevices() {
+ auto serviceManager = IServiceManager::getService();
+ CHECK(serviceManager) << "Could not retrieve ServiceManager";
+
+ auto km4s = enumerateDevices<Keymaster4>(serviceManager);
+ auto km3s = enumerateDevices<Keymaster3>(serviceManager);
+
+ auto result = std::move(km4s);
+ result.insert(result.end(), std::make_move_iterator(km3s.begin()),
+ std::make_move_iterator(km3s.end()));
+
+ std::sort(result.begin(), result.end(),
+ [](auto& a, auto& b) { return a->halVersion() > b->halVersion(); });
+
+ size_t i = 1;
+ LOG(INFO) << "List of Keymaster HALs found:";
+ for (auto& hal : result) {
+ auto& version = hal->halVersion();
+ LOG(INFO) << "Keymaster HAL #" << i << ": " << version.keymasterName << " from "
+ << version.authorName << " SecurityLevel: " << toString(version.securityLevel)
+ << " HAL : " << hal->descriptor() << " instance " << hal->instanceName();
+ }
+
+ return result;
+}
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+}; // namespace android
diff --git a/keymaster/4.0/support/Keymaster3.cpp b/keymaster/4.0/support/Keymaster3.cpp
index 6dfe85b..b2cdbd9 100644
--- a/keymaster/4.0/support/Keymaster3.cpp
+++ b/keymaster/4.0/support/Keymaster3.cpp
@@ -18,7 +18,7 @@
#include <keymasterV4_0/Keymaster3.h>
#include <android-base/logging.h>
-#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/keymaster_utils.h>
namespace android {
namespace hardware {
@@ -82,35 +82,6 @@
return std::copy(value_ptr, value_ptr + sizeof(value), dest);
}
-constexpr size_t kHmacSize = 32;
-
-inline static hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
- static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
- "HardwareAuthToken content size does not match hw_auth_token_t size");
-
- hidl_vec<uint8_t> result;
- result.resize(sizeof(hw_auth_token_t));
- auto pos = result.begin();
- *pos++ = 0; // Version byte
- pos = copy_bytes_to_iterator(token.challenge, pos);
- pos = copy_bytes_to_iterator(token.userId, pos);
- pos = copy_bytes_to_iterator(token.authenticatorId, pos);
- auto auth_type = htonl(static_cast<uint32_t>(token.authenticatorType));
- pos = copy_bytes_to_iterator(auth_type, pos);
- auto timestamp = htonq(token.timestamp);
- pos = copy_bytes_to_iterator(timestamp, pos);
- if (token.mac.size() != kHmacSize) {
- std::fill(pos, pos + kHmacSize, 0);
- } else {
- std::copy(token.mac.begin(), token.mac.end(), pos);
- }
-
- return result;
-}
-
hidl_vec<V3_0::KeyParameter> convertAndAddAuthToken(const hidl_vec<KeyParameter>& params,
const HardwareAuthToken& authToken) {
hidl_vec<V3_0::KeyParameter> converted(params.size() + 1);
@@ -139,37 +110,32 @@
[&](bool isSecure, bool supportsEllipticCurve, bool supportsSymmetricCryptography,
bool supportsAttestation, bool supportsAllDigests, const hidl_string& keymasterName,
const hidl_string& keymasterAuthorName) {
- securityLevel_ =
- isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE;
- supportsEllipticCurve_ = supportsEllipticCurve;
+ version_ = {keymasterName, keymasterAuthorName, 0 /* major version, filled below */,
+ isSecure ? SecurityLevel::TRUSTED_ENVIRONMENT : SecurityLevel::SOFTWARE,
+ supportsEllipticCurve};
supportsSymmetricCryptography_ = supportsSymmetricCryptography;
supportsAttestation_ = supportsAttestation;
supportsAllDigests_ = supportsAllDigests;
- keymasterName_ = keymasterName;
- authorName_ = keymasterAuthorName;
});
CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware features";
- if (securityLevel_ == SecurityLevel::SOFTWARE) {
- majorVersion_ = 3;
+ if (version_.securityLevel == SecurityLevel::SOFTWARE) {
+ version_.majorVersion = 3;
} else if (supportsAttestation_) {
- majorVersion_ = 3; // Could be 2, doesn't matter.
+ version_.majorVersion = 3; // Could be 2, doesn't matter.
} else if (supportsSymmetricCryptography_) {
- majorVersion_ = 1;
+ version_.majorVersion = 1;
} else {
- majorVersion_ = 0;
+ version_.majorVersion = 0;
}
}
-Keymaster::VersionResult Keymaster3::halVersion() {
- getVersionIfNeeded();
- return {ErrorCode::OK, majorVersion_, securityLevel_, supportsEllipticCurve_};
-}
-
Return<void> Keymaster3::getHardwareInfo(Keymaster3::getHardwareInfo_cb _hidl_cb) {
getVersionIfNeeded();
- _hidl_cb(securityLevel_, keymasterName_ + " (wrapped by keystore::Keymaster3)", authorName_);
+ _hidl_cb(version_.securityLevel,
+ std::string(version_.keymasterName) + " (wrapped by keystore::Keymaster3)",
+ version_.authorName);
return Void();
}
diff --git a/keymaster/4.0/support/Keymaster4.cpp b/keymaster/4.0/support/Keymaster4.cpp
index fdf78ae..cc3d656 100644
--- a/keymaster/4.0/support/Keymaster4.cpp
+++ b/keymaster/4.0/support/Keymaster4.cpp
@@ -28,19 +28,17 @@
void Keymaster4::getVersionIfNeeded() {
if (haveVersion_) return;
- auto rc = dev_->getHardwareInfo([&](SecurityLevel securityLevel, auto...) {
- securityLevel_ = securityLevel;
- haveVersion_ = true;
- });
+ auto rc =
+ dev_->getHardwareInfo([&](SecurityLevel securityLevel, const hidl_string& keymasterName,
+ const hidl_string& authorName) {
+ version_ = {keymasterName, authorName, 4 /* major version */, securityLevel,
+ true /* supportsEc */};
+ haveVersion_ = true;
+ });
CHECK(rc.isOk()) << "Got error " << rc.description() << " trying to get hardware info";
}
-Keymaster::VersionResult Keymaster4::halVersion() {
- getVersionIfNeeded();
- return {ErrorCode::OK, halMajorVersion(), securityLevel_, true};
-}
-
} // namespace support
} // namespace V4_0
} // namespace keymaster
diff --git a/keymaster/4.0/support/authorization_set.cpp b/keymaster/4.0/support/authorization_set.cpp
index 81cf365..bf77420 100644
--- a/keymaster/4.0/support/authorization_set.cpp
+++ b/keymaster/4.0/support/authorization_set.cpp
@@ -503,6 +503,18 @@
return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
}
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
+ return BlockMode(BlockMode::GCM)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_MAC_LENGTH, macLength);
+}
+
AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
std::initializer_list<V4_0::BlockMode> blockModes) {
for (auto mode : blockModes) {
@@ -519,6 +531,14 @@
return *this;
}
+AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
+ std::initializer_list<V4_0::PaddingMode> paddingModes) {
+ for (auto paddingMode : paddingModes) {
+ push_back(TAG_PADDING, paddingMode);
+ }
+ return *this;
+}
+
} // namespace V4_0
} // namespace keymaster
} // namespace hardware
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
index 2686fcd..f9efd51 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster.h
@@ -37,16 +37,37 @@
*/
class Keymaster : public IKeymasterDevice {
public:
+ Keymaster(const hidl_string& descriptor, const hidl_string& instanceName)
+ : descriptor_(descriptor), instanceName_(instanceName) {}
virtual ~Keymaster() {}
struct VersionResult {
- ErrorCode error;
+ hidl_string keymasterName;
+ hidl_string authorName;
uint8_t majorVersion;
SecurityLevel securityLevel;
bool supportsEc;
+
+ bool operator>(const VersionResult& other) const {
+ auto lhs = std::tie(securityLevel, majorVersion, supportsEc);
+ auto rhs = std::tie(other.securityLevel, other.majorVersion, other.supportsEc);
+ return lhs > rhs;
+ }
};
- virtual VersionResult halVersion() = 0;
+ virtual const VersionResult& halVersion() = 0;
+ const hidl_string& descriptor() { return descriptor_; }
+ const hidl_string& instanceName() { return instanceName_; }
+
+ /**
+ * Returns all available Keymaster3 and Keymaster4 instances, in order of most secure to least
+ * secure (as defined by VersionResult::operator<).
+ */
+ static std::vector<std::unique_ptr<Keymaster>> enumerateAvailableDevices();
+
+ private:
+ hidl_string descriptor_;
+ hidl_string instanceName_;
};
} // namespace support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
index 4054620..2bb77ca 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster3.h
@@ -40,9 +40,15 @@
class Keymaster3 : public Keymaster {
public:
using WrappedIKeymasterDevice = IKeymaster3Device;
- Keymaster3(sp<IKeymaster3Device> km3_dev) : km3_dev_(km3_dev), haveVersion_(false) {}
+ Keymaster3(sp<IKeymaster3Device> km3_dev, const hidl_string& instanceName)
+ : Keymaster(IKeymaster3Device::descriptor, instanceName),
+ km3_dev_(km3_dev),
+ haveVersion_(false) {}
- VersionResult halVersion() override;
+ const VersionResult& halVersion() override {
+ getVersionIfNeeded();
+ return version_;
+ }
Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb);
@@ -114,14 +120,10 @@
sp<IKeymaster3Device> km3_dev_;
bool haveVersion_;
- uint8_t majorVersion_;
- SecurityLevel securityLevel_;
- bool supportsEllipticCurve_;
+ VersionResult version_;
bool supportsSymmetricCryptography_;
bool supportsAttestation_;
bool supportsAllDigests_;
- std::string keymasterName_;
- std::string authorName_;
};
} // namespace support
diff --git a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
index 86ef4f8..96afb13 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/Keymaster4.h
@@ -32,11 +32,15 @@
class Keymaster4 : public Keymaster {
public:
using WrappedIKeymasterDevice = IKeymaster4Device;
- Keymaster4(sp<IKeymasterDevice> km4_dev) : haveVersion_(false), dev_(km4_dev) {}
+ Keymaster4(sp<IKeymasterDevice> km4_dev, const hidl_string& instanceName)
+ : Keymaster(IKeymaster4Device::descriptor, instanceName),
+ haveVersion_(false),
+ dev_(km4_dev) {}
- uint8_t halMajorVersion() { return 4; }
-
- VersionResult halVersion() override;
+ const VersionResult& halVersion() override {
+ getVersionIfNeeded();
+ return version_;
+ }
Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override {
return dev_->getHardwareInfo(_hidl_cb);
@@ -143,7 +147,7 @@
void getVersionIfNeeded();
bool haveVersion_;
- SecurityLevel securityLevel_;
+ VersionResult version_;
sp<IKeymaster4Device> dev_;
};
diff --git a/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h b/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
index 09a06fe..6c7fd35 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/authorization_set.h
@@ -270,11 +270,16 @@
AuthorizationSetBuilder& SigningKey();
AuthorizationSetBuilder& EncryptionKey();
+
AuthorizationSetBuilder& NoDigestOrPadding();
+
AuthorizationSetBuilder& EcbMode();
+ AuthorizationSetBuilder& GcmModeMinMacLen(uint32_t minMacLength);
+ AuthorizationSetBuilder& GcmModeMacLen(uint32_t macLength);
AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> blockModes);
AuthorizationSetBuilder& Digest(std::initializer_list<Digest> digests);
+ AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
template <typename... T>
AuthorizationSetBuilder& BlockMode(T&&... a) {
@@ -288,10 +293,6 @@
AuthorizationSetBuilder& Padding(T&&... a) {
return Padding({std::forward<T>(a)...});
}
-
- AuthorizationSetBuilder& Padding(PaddingMode padding) {
- return Authorization(TAG_PADDING, padding);
- }
};
} // namespace V4_0
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index 73e03fb..ce213bc 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -104,61 +104,66 @@
typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
static TAG_##name##_t TAG_##name;
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
DECLARE_TYPED_TAG(INVALID);
DECLARE_TYPED_TAG(KEY_SIZE);
DECLARE_TYPED_TAG(MAC_LENGTH);
-DECLARE_TYPED_TAG(CALLER_NONCE);
-DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
-DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
-DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
-DECLARE_TYPED_TAG(ACTIVE_DATETIME);
-DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
-DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
-DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(NONCE);
DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
-DECLARE_TYPED_TAG(AUTH_TIMEOUT);
-DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
-DECLARE_TYPED_TAG(APPLICATION_ID);
-DECLARE_TYPED_TAG(APPLICATION_DATA);
-DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
DECLARE_TYPED_TAG(ROOT_OF_TRUST);
-DECLARE_TYPED_TAG(ASSOCIATED_DATA);
-DECLARE_TYPED_TAG(NONCE);
-DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
-DECLARE_TYPED_TAG(OS_VERSION);
-DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
DECLARE_TYPED_TAG(UNIQUE_ID);
-DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
-DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
-DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
-
-DECLARE_TYPED_TAG(PURPOSE);
-DECLARE_TYPED_TAG(ALGORITHM);
-DECLARE_TYPED_TAG(BLOCK_MODE);
-DECLARE_TYPED_TAG(DIGEST);
-DECLARE_TYPED_TAG(PADDING);
-DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
-DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(UNLOCKED_DEVICE_REQUIRED);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
DECLARE_TYPED_TAG(USER_AUTH_TYPE);
-DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(USER_ID);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
template <typename... Elems>
struct MetaList {};
-using all_tags_t = MetaList<
- TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
- TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
- TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t, TAG_MIN_SECONDS_BETWEEN_OPS_t,
- TAG_MAX_USES_PER_BOOT_t, TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t,
- TAG_ALLOW_WHILE_ON_BODY_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t,
- TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t,
- TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t,
- TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t, TAG_RESET_SINCE_ID_ROTATION_t,
- TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t, TAG_DIGEST_t, TAG_PADDING_t,
- TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t>;
+using all_tags_t =
+ MetaList<TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t,
+ TAG_MIN_MAC_LENGTH_t, TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t,
+ TAG_ACTIVE_DATETIME_t, TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+ TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t,
+ TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t,
+ TAG_ALLOW_WHILE_ON_BODY_t, TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t,
+ TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t,
+ TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t,
+ TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, TAG_ATTESTATION_CHALLENGE_t,
+ TAG_ATTESTATION_APPLICATION_ID_t, TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t,
+ TAG_ALGORITHM_t, TAG_BLOCK_MODE_t, TAG_DIGEST_t, TAG_PADDING_t,
+ TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t>;
template <typename TypedTagType>
struct TypedTag2ValueType;
@@ -342,8 +347,10 @@
case Tag::BOOTLOADER_ONLY:
case Tag::NO_AUTH_REQUIRED:
case Tag::ALLOW_WHILE_ON_BODY:
+ case Tag::UNLOCKED_DEVICE_REQUIRED:
case Tag::ROLLBACK_RESISTANCE:
case Tag::RESET_SINCE_ID_ROTATION:
+ case Tag::TRUSTED_CONFIRMATION_REQUIRED:
case Tag::TRUSTED_USER_PRESENCE_REQUIRED:
return true;
@@ -355,7 +362,10 @@
case Tag::OS_VERSION:
case Tag::OS_PATCHLEVEL:
case Tag::MAC_LENGTH:
+ case Tag::USER_ID:
case Tag::AUTH_TIMEOUT:
+ case Tag::VENDOR_PATCHLEVEL:
+ case Tag::BOOT_PATCHLEVEL:
return a.f.integer == b.f.integer;
/* Long integer tags */
@@ -386,6 +396,7 @@
case Tag::ATTESTATION_ID_MANUFACTURER:
case Tag::ATTESTATION_ID_MODEL:
case Tag::ASSOCIATED_DATA:
+ case Tag::CONFIRMATION_TOKEN:
case Tag::NONCE:
return a.blob == b.blob;
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
new file mode 100644
index 0000000..1c1b000
--- /dev/null
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_utils.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
+#define HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
+
+#include <android/hardware/keymaster/4.0/types.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const uint8_t* data, const size_t length,
+ bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<unsigned char*>(data), length, !inPlace);
+ return result;
+}
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const std::string& value, bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(value.data())),
+ static_cast<size_t>(value.size()), !inPlace);
+ return result;
+}
+
+inline static hidl_vec<uint8_t> blob2hidlVec(const std::vector<uint8_t>& blob,
+ bool inPlace = true) {
+ hidl_vec<uint8_t> result;
+ result.setToExternal(const_cast<uint8_t*>(blob.data()), static_cast<size_t>(blob.size()),
+ !inPlace);
+ return result;
+}
+
+HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer);
+hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token);
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
+
+#endif // HARDWARE_INTERFACES_KEYMASTER_40_SUPPORT_KEYMASTER_UTILS_H_
diff --git a/keymaster/4.0/support/keymaster_utils.cpp b/keymaster/4.0/support/keymaster_utils.cpp
new file mode 100644
index 0000000..bc610aa
--- /dev/null
+++ b/keymaster/4.0/support/keymaster_utils.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace support {
+
+template <typename T, typename InIter>
+inline static InIter copy_bytes_from_iterator(T* value, InIter src) {
+ uint8_t* value_ptr = reinterpret_cast<uint8_t*>(value);
+ std::copy(src, src + sizeof(T), value_ptr);
+ return src + sizeof(T);
+}
+
+template <typename T, typename OutIter>
+inline static OutIter copy_bytes_to_iterator(const T& value, OutIter dest) {
+ const uint8_t* value_ptr = reinterpret_cast<const uint8_t*>(&value);
+ return std::copy(value_ptr, value_ptr + sizeof(value), dest);
+}
+
+constexpr size_t kHmacSize = 32;
+
+hidl_vec<uint8_t> authToken2HidlVec(const HardwareAuthToken& token) {
+ static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
+ "HardwareAuthToken content size does not match hw_auth_token_t size");
+
+ hidl_vec<uint8_t> result;
+ result.resize(sizeof(hw_auth_token_t));
+ auto pos = result.begin();
+ *pos++ = 0; // Version byte
+ pos = copy_bytes_to_iterator(token.challenge, pos);
+ pos = copy_bytes_to_iterator(token.userId, pos);
+ pos = copy_bytes_to_iterator(token.authenticatorId, pos);
+ auto auth_type = htonl(static_cast<uint32_t>(token.authenticatorType));
+ pos = copy_bytes_to_iterator(auth_type, pos);
+ auto timestamp = htonq(token.timestamp);
+ pos = copy_bytes_to_iterator(timestamp, pos);
+ if (token.mac.size() != kHmacSize) {
+ std::fill(pos, pos + kHmacSize, 0);
+ } else {
+ std::copy(token.mac.begin(), token.mac.end(), pos);
+ }
+
+ return result;
+}
+
+HardwareAuthToken hidlVec2AuthToken(const hidl_vec<uint8_t>& buffer) {
+ HardwareAuthToken token;
+ static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
+ sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
+ sizeof(token.timestamp) + kHmacSize ==
+ sizeof(hw_auth_token_t),
+ "HardwareAuthToken content size does not match hw_auth_token_t size");
+
+ if (buffer.size() != sizeof(hw_auth_token_t)) return {};
+
+ auto pos = buffer.begin();
+ ++pos; // skip first byte
+ pos = copy_bytes_from_iterator(&token.challenge, pos);
+ pos = copy_bytes_from_iterator(&token.userId, pos);
+ pos = copy_bytes_from_iterator(&token.authenticatorId, pos);
+ pos = copy_bytes_from_iterator(&token.authenticatorType, pos);
+ token.authenticatorType = static_cast<HardwareAuthenticatorType>(
+ ntohl(static_cast<uint32_t>(token.authenticatorType)));
+ pos = copy_bytes_from_iterator(&token.timestamp, pos);
+ token.timestamp = ntohq(token.timestamp);
+ token.mac.resize(kHmacSize);
+ std::copy(pos, pos + kHmacSize, token.mac.data());
+
+ return token;
+}
+
+} // namespace support
+} // namespace V4_0
+} // namespace keymaster
+} // namespace hardware
+} // namespace android
diff --git a/keymaster/4.0/types.hal b/keymaster/4.0/types.hal
index e890c6d..47fd1ed 100644
--- a/keymaster/4.0/types.hal
+++ b/keymaster/4.0/types.hal
@@ -118,7 +118,8 @@
* boot. */
/* User authentication */
- // 500-501 reserved
+ // 500 reserved
+ USER_ID = TagType:UINT | 501, /* Android ID of authorized user or authenticator(s), */
USER_SECURE_ID = TagType:ULONG_REP | 502, /* Secure ID of authorized user or authenticator(s).
* Disallowed if NO_AUTH_REQUIRED is present. */
NO_AUTH_REQUIRED = TagType:BOOL | 503, /* If key is usable without authentication. */
@@ -181,6 +182,19 @@
*/
TRUSTED_USER_PRESENCE_REQUIRED = TagType:BOOL | 507,
+ /** TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and specifies
+ * that this key must not be usable unless the user provides confirmation of the data to be
+ * signed. Confirmation is proven to keymaster via an approval token. See CONFIRMATION_TOKEN,
+ * as well as the ConfirmatinUI HAL.
+ *
+ * If an attempt to use a key with this tag does not have a cryptographically valid
+ * CONFIRMATION_TOKEN provided to finish() or if the data provided to update()/finish() does not
+ * match the data described in the token, keymaster must return NO_USER_CONFIRMATION. */
+ TRUSTED_CONFIRMATION_REQUIRED = TagType:BOOL | 508,
+
+ UNLOCKED_DEVICE_REQUIRED = TagType:BOOL | 509, /* Require the device screen to be unlocked if
+ * the key is used. */
+
/* Application access control */
APPLICATION_ID = TagType:BYTES | 601, /* Byte string identifying the authorized application. */
@@ -219,6 +233,30 @@
ATTESTATION_ID_MODEL = TagType:BYTES | 717, /* Used to provide the device's model name to be
* included in attestation */
+ /**
+ * Patch level of vendor image. The value is an integer of the form YYYYMM, where YYYY is the
+ * four-digit year when the vendor image was released and MM is the two-digit month. During
+ * each boot, the bootloader must provide the patch level of the vendor image to keymaser
+ * (mechanism is implemntation-defined). When keymaster keys are created or updated, the
+ * VENDOR_PATCHLEVEL tag must be cryptographically bound to the keys, with the current value as
+ * provided by the bootloader. When keys are used, keymaster must verify that the
+ * VENDOR_PATCHLEVEL bound to the key matches the current value. If they do not match,
+ * keymaster must return ErrorCode::KEY_REQUIRES_UPGRADE. The client must then call upgradeKey.
+ */
+ VENDOR_PATCHLEVEL = TagType:UINT | 718,
+
+ /**
+ * Patch level of boot image. The value is an integer of the form YYYYMM, where YYYY is the
+ * four-digit year when the boot image was released and MM is the two-digit month. During each
+ * boot, the bootloader must provide the patch level of the boot image to keymaser (mechanism is
+ * implemntation-defined). When keymaster keys are created or updated, the BOOT_PATCHLEVEL tag
+ * must be cryptographically bound to the keys, with the current value as provided by the
+ * bootloader. When keys are used, keymaster must verify that the BOOT_PATCHLEVEL bound to the
+ * key matches the current value. If they do not match, keymaster must return
+ * ErrorCode::KEY_REQUIRES_UPGRADE. The client must then call upgradeKey.
+ */
+ BOOT_PATCHLEVEL = TagType:UINT | 719,
+
/* Tags used only to provide data to or receive data from operations */
ASSOCIATED_DATA = TagType:BYTES | 1000, /* Used to provide associated data for AEAD modes. */
NONCE = TagType:BYTES | 1001, /* Nonce or Initialization Vector */
@@ -227,6 +265,13 @@
RESET_SINCE_ID_ROTATION = TagType:BOOL | 1004, /* Whether the device has beeen factory reset
* since the last unique ID rotation. Used for
* key attestation. */
+
+ /**
+ * CONFIRMATION_TOKEN is used to deliver a cryptographic token proving that the user confirmed a
+ * signing request. The content is a full-length HMAC-SHA256 value. See the ConfirmationUI HAL
+ * for details of token computation.
+ */
+ CONFIRMATION_TOKEN = TagType:BYTES | 1005,
};
/**
@@ -429,6 +474,8 @@
HARDWARE_TYPE_UNAVAILABLE = -68,
PROOF_OF_PRESENCE_REQUIRED = -69,
CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+ NO_USER_CONFIRMATION = -71,
+ DEVICE_LOCKED = -72,
UNIMPLEMENTED = -100,
VERSION_MISMATCH = -101,
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index 1d8dfdf..dbf5ece 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -712,6 +712,29 @@
}
/*
+ * SigningOperationsTest.NoUserConfirmation
+ *
+ * Verifies that keymaster rejects signing operations for keys with
+ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token
+ * presented.
+ */
+TEST_F(SigningOperationsTest, NoUserConfirmation) {
+ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 3)
+ .Digest(Digest::NONE)
+ .Padding(PaddingMode::NONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)));
+
+ const string message = "12345678901234567890123456789012";
+ EXPECT_EQ(ErrorCode::OK,
+ Begin(KeyPurpose::SIGN,
+ AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+ string signature;
+ EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature));
+}
+
+/*
* SigningOperationsTest.RsaPkcs1Sha256Success
*
* Verifies that digested RSA-PKCS1 signature operations succeed.
diff --git a/light/utils/blank_screen.rc b/light/utils/blank_screen.rc
index 735551c..7b2a55e 100644
--- a/light/utils/blank_screen.rc
+++ b/light/utils/blank_screen.rc
@@ -1,5 +1,5 @@
service blank_screen /system/bin/blank_screen
user system
oneshot
- group system readproc
+ group system
shutdown critical
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp b/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
index b3d5648..d0320b9 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
@@ -489,4 +489,4 @@
RadioError::MODEM_ERR, RadioError::OPERATION_NOT_ALLOWED},
CHECK_GENERAL_ERROR));
}
-}
\ No newline at end of file
+}
diff --git a/radio/1.2/Android.bp b/radio/1.2/Android.bp
index 56e4afd..f4ca1cf 100644
--- a/radio/1.2/Android.bp
+++ b/radio/1.2/Android.bp
@@ -20,6 +20,7 @@
],
types: [
"CardStatus",
+ "CellConnectionStatus",
"CellIdentityCdma",
"CellIdentityGsm",
"CellIdentityLte",
@@ -31,9 +32,12 @@
"CellInfoLte",
"CellInfoWcdma",
"IncrementalResultsPeriodicityRange",
+ "IndicationFilter",
+ "LinkCapacityEstimate",
"MaxSearchTimeRange",
"NetworkScanRequest",
"NetworkScanResult",
+ "PhysicalChannelConfig",
"RadioConst",
"ScanIntervalRange",
],
diff --git a/radio/1.2/IRadio.hal b/radio/1.2/IRadio.hal
index 6ae78a0..67ce56c 100644
--- a/radio/1.2/IRadio.hal
+++ b/radio/1.2/IRadio.hal
@@ -17,6 +17,9 @@
package android.hardware.radio@1.2;
import @1.1::IRadio;
+import @1.1::RadioAccessNetworks;
+import @1.0::DataProfileInfo;
+import @1.0::RadioTechnology;
/**
* This interface is used by telephony and telecom to talk to cellular radio.
@@ -37,4 +40,135 @@
* Response function is IRadioResponse.startNetworkScanResponse()
*/
oneway startNetworkScan_1_2(int32_t serial, NetworkScanRequest request);
+
+ /**
+ * Sets the indication filter.
+ *
+ * Prevents the reporting of specified unsolicited indications from the radio. This is used
+ * for power saving in instances when those indications are not needed. If unset, defaults to
+ * @1.2::IndicationFilter:ALL.
+ *
+ * @param serial Serial number of request.
+ * @param indicationFilter 32-bit bitmap of IndicationFilter. Bits set to 1 indicate the
+ * indications are enabled. See @1.2::IndicationFilter for the definition of each bit.
+ *
+ * Response callback is IRadioResponse.setIndicationFilterResponse()
+ */
+ oneway setIndicationFilter_1_2(int32_t serial, bitfield<IndicationFilter> indicationFilter);
+
+ /**
+ * Sets the signal strength reporting criteria.
+ *
+ * The resulting reporting criteria are the AND of all the supplied criteria.
+ *
+ * Note: Reporting criteria must be individually set for each RAN. If unset, reporting criteria
+ * for that RAN are implementation-defined.
+ *
+ * Response callback is IRadioResponse.setSignalStrengthReportingCriteriaResponse().
+ *
+ * @param serial Serial number of request.
+ * @param hysteresisMs A hysteresis time in milliseconds to prevent flapping. A value of 0
+ * disables hysteresis.
+ * @param hysteresisDb An interval in dB defining the required magnitude change between reports.
+ * hysteresisDb must be smaller than the smallest threshold delta. An
+ * interval value of 0 disables hysteresis.
+ * @param thresholdsDbm A vector of trigger thresholds in dBm. A vector size of 0 disables the
+ * use of thresholds for reporting.
+ * @param ran The type of network for which to apply these thresholds.
+ */
+ oneway setSignalStrengthReportingCriteria(int32_t serial, int32_t hysteresisMs,
+ int32_t hysteresisDb, vec<int32_t> thresholdsDbm, RadioAccessNetworks ran);
+
+ /**
+ * Sets the link capacity reporting criteria.
+ *
+ * The resulting reporting criteria are the AND of all the supplied criteria.
+ *
+ * Note: Reporting criteria must be individually set for each RAN. If unset, reporting criteria
+ * for that RAN are implementation-defined.
+ *
+ * Response callback is IRadioResponse.setLinkCapacityReportingCriteriaResponse().
+ *
+ * @param serial Serial number of request.
+ * @param hysteresisMs A hysteresis time in milliseconds to prevent flapping. A value of 0
+ * disables hysteresis.
+ * @param hysteresisDlKbps An interval in kbps defining the required magnitude change between DL
+ * reports. hysteresisDlKbps must be smaller than the smallest threshold
+ * delta. A value of 0 disables hysteresis.
+ * @param hysteresisUlKbps An interval in kbps defining the required magnitude change between UL
+ * reports. hysteresisUlKbps must be smaller than the smallest threshold
+ * delta. A value of 0 disables hysteresis.
+ * @param thresholdsDownlinkKbps A vector of trigger thresholds in kbps for downlink reports. A
+ * vector size of 0 disables the use of DL thresholds for
+ * reporting.
+ * @param thresholdsUplinkKbps A vector of trigger thresholds in kbps for uplink reports. A
+ * vector size of 0 disables the use of UL thresholds for reporting.
+ * @param ran The type of network for which to apply these thresholds.
+ */
+ oneway setLinkCapacityReportingCriteria(int32_t serial, int32_t hysteresisMs,
+ int32_t hysteresisDlKbps, int32_t hysteresisUlKbps, vec<int32_t> thresholdsDownlinkKbps,
+ vec<int32_t> thresholdsUplinkKbps, RadioAccessNetworks ran);
+
+ /**
+ * Setup a packet data connection. If DataCallResponse.status returns DataCallFailCause:NONE,
+ * the data connection must be added to data calls and a unsolDataCallListChanged() must be
+ * sent. The call remains until removed by subsequent unsolDataCallIstChanged(). It may be
+ * lost due to many factors, including deactivateDataCall() being issued, the radio powered
+ * off, reception lost or even transient factors like congestion. This data call list is
+ * returned by getDataCallList() and dataCallListChanged().
+ *
+ * The Radio is expected to:
+ * - Create one data call context.
+ * - Create and configure a dedicated interface for the context.
+ * - The interface must be point to point.
+ * - The interface is configured with one or more addresses and is capable of sending and
+ * receiving packets. The prefix length of the addresses must be /32 for IPv4 and /128
+ * for IPv6.
+ * - Must not modify routing configuration related to this interface; routing management is
+ * exclusively within the purview of the Android OS.
+ * - Support simultaneous data call contexts up to DataRegStateResult.maxDataCalls specified
+ * in the response of getDataRegistrationState.
+ *
+ * @param serial Serial number of request.
+ * @param accessNetwork The access network to setup the data call. If the data connection cannot
+ * be established on the specified access network, the setup request must be failed.
+ * @param dataProfileInfo Data profile info.
+ * @param modemCognitive Indicates that the requested profile has previously been provided via
+ * setDataProfile().
+ * @param roamingAllowed Indicates whether or not data roaming is allowed by the user.
+ * @param isRoaming Indicates whether or not the framework has requested this setupDataCall for
+ * a roaming network. The 'protocol' parameter in the old RIL API must be filled
+ * accordingly based on the roaming condition. Note this is for backward compatibility with
+ * the old radio modem. The modem must not use this param for any other reason.
+ * @param reason The request reason. Must be DataRequestReason.NORMAL or
+ * DataRequestReason.HANDOVER.
+ * @param addresses If the reason is DataRequestReason.HANDOVER, this indicates the list of link
+ * addresses of the existing data connection. The format is IP address with optional "/"
+ * prefix length (The format is defined in RFC-4291 section 2.3). For example, "192.0.1.3",
+ * "192.0.1.11/16", or "2001:db8::1/64". Typically one IPv4 or one IPv6 or one of each. If
+ * the prefix length is absent, then the addresses are assumed to be point to point with
+ * IPv4 with prefix length 32 or IPv6 with prefix length 128. This parameter must be ignored
+ * unless reason is DataRequestReason.HANDOVER.
+ * @param dnses If the reason is DataRequestReason.HANDOVER, this indicates the list of DNS
+ * addresses of the existing data connection. The format is defined in RFC-4291 section
+ * 2.2. For example, "192.0.1.3" or "2001:db8::1". This parameter must be ignored unless
+ * reason is DataRequestReason.HANDOVER.
+ *
+ * Response function is IRadioResponse.setupDataCallResponse()
+ */
+ oneway setupDataCall_1_2(int32_t serial, AccessNetwork accessNetwork,
+ DataProfileInfo dataProfileInfo, bool modemCognitive, bool roamingAllowed,
+ bool isRoaming, DataRequestReason reason, vec<string> addresses, vec<string> dnses);
+
+ /**
+ * Deactivate packet data connection and remove from the data call list. An
+ * unsolDataCallListChanged() must be sent when data connection is deactivated.
+ *
+ * @param serial Serial number of request.
+ * @param cid Data call id.
+ * @param reason The request reason. Must be normal, handover, or shutdown.
+ *
+ * Response function is IRadioResponse.deactivateDataCallResponse()
+ */
+ oneway deactivateDataCall_1_2(int32_t serial, int32_t cid, DataRequestReason reason);
};
diff --git a/radio/1.2/IRadioIndication.hal b/radio/1.2/IRadioIndication.hal
index 22f655c..a124557 100644
--- a/radio/1.2/IRadioIndication.hal
+++ b/radio/1.2/IRadioIndication.hal
@@ -37,4 +37,29 @@
* @param records Current cell information known to radio
*/
oneway cellInfoList_1_2(RadioIndicationType type, vec<CellInfo> records);
+
+ /**
+ * Indicates current link capacity estimate.
+ *
+ * This replaces @1.0::IRadioIndication.lceData(). The framework must be able to handle
+ * either this function or @1.0::IRadioIndication.lceData(). Implementations supporting
+ * v1.2 must call this function instead of lceData().
+ *
+ * This indication is sent whenever the reporting criteria, as set by
+ * @1.2::IRadio.setLinkCapacityReportingCriteria, are met and the indication is not
+ * suppressed by @1.2::IRadio.setIndicationFilter_1_2().
+ *
+ * @param type Type of radio indication
+ * @param lce LinkCapacityEstimate information as defined in types.hal
+ */
+ oneway currentLinkCapacityEstimate(RadioIndicationType type, LinkCapacityEstimate lce);
+
+ /**
+ * Indicates physical channel configurations.
+ *
+ * @param type Type of radio indication
+ * @param configs List of PhysicalChannelConfigs as defined in types.hal
+ */
+ oneway currentPhysicalChannelConfigs(RadioIndicationType type,
+ vec<PhysicalChannelConfig> configs);
};
diff --git a/radio/1.2/IRadioResponse.hal b/radio/1.2/IRadioResponse.hal
index a7ad30c..c356954 100644
--- a/radio/1.2/IRadioResponse.hal
+++ b/radio/1.2/IRadioResponse.hal
@@ -50,4 +50,26 @@
* RadioError:NONE
*/
oneway getIccCardStatusResponse_1_2(RadioResponseInfo info, CardStatus cardStatus);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway setSignalStrengthReportingCriteriaResponse(RadioResponseInfo info);
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ *
+ * Valid errors returned:
+ * RadioError:NONE
+ * RadioError:INVALID_ARGUMENTS
+ * RadioError:RADIO_NOT_AVAILABLE
+ * RadioError:INTERNAL_ERR
+ */
+ oneway setLinkCapacityReportingCriteriaResponse(RadioResponseInfo info);
};
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 3aa2446..f2f0b69 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -64,6 +64,67 @@
MAX = 10,
};
+enum CellConnectionStatus : int32_t {
+ /**
+ * Cell is not a serving cell.
+ */
+ NONE = 0,
+ /**
+ * UE has connection to cell for signalling and possibly data (3GPP 36.331, 25.331).
+ */
+ PRIMARY_SERVING,
+ /**
+ * UE has connection to cell for data (3GPP 36.331, 25.331).
+ */
+ SECONDARY_SERVING,
+};
+
+/**
+ * Overwritten from @1.0::IndicationFilter in order to redefine ALL. In the future, this should
+ * be extended instead of overwritten.
+ */
+enum IndicationFilter : int32_t {
+ NONE = 0,
+ ALL = ~0,
+ /**
+ * When this bit is set, modem must send the signal strength update through
+ * IRadioIndication.currentSignalStrength() when all criteria specified by
+ * IRadio.setSignalStrengthReportingCriteria() are met.
+ */
+ SIGNAL_STRENGTH = 1 << 0,
+ /**
+ * When this bit is set, modem must invoke IRadioIndication.networkStateChanged() when any field
+ * in VoiceRegStateResult or DataRegStateResult changes. When this bit is not set, modem must
+ * suppress IRadioIndication.networkStateChanged() when there are only changes from
+ * insignificant fields. Modem must invoke IRadioIndication.networkStateChanged() when
+ * significant fields are updated regardless of whether this bit is set.
+ *
+ * The following fields are considered significant: VoiceRegStateResult.regState,
+ * VoiceRegStateResult.rat, DataRegStateResult.regState, DataRegStateResult.rat.
+ */
+ FULL_NETWORK_STATE = 1 << 1,
+ /**
+ * When this bit is set, modem must send IRadioIndication.dataCallListChanged() whenever any
+ * field in ITypes.SetupDataCallResult changes. When this bit is not set, modem must suppress
+ * the indication when the only changed field is 'active' (for data dormancy). For all other
+ * field changes, the modem must send IRadioIndication.dataCallListChanged() regardless of
+ * whether this bit is set.
+ */
+ DATA_CALL_DORMANCY_CHANGED = 1 << 2,
+ /**
+ * When this bit is set, modem must send the link capacity update through
+ * IRadioIndication.currentLinkCapacityEstimate() when all criteria specified by
+ * IRadio.setLinkCapacityReportingCriteria() are met.
+ */
+ LINK_CAPACITY_ESTIMATE = 1 << 3,
+ /**
+ * When this bit is set, the modem must send the physical channel configuration update through
+ * IRadioIndication.currentPhysicalChannelConfigs() when the configuration has changed. It is
+ * recommended that this be reported whenever link capacity or signal strength is reported.
+ */
+ PHYSICAL_CHANNEL_CONFIG = 1 << 4,
+};
+
struct NetworkScanRequest {
ScanType type;
@@ -156,6 +217,10 @@
struct CellIdentityLte {
@1.0::CellIdentityLte base;
CellIdentityOperatorNames operatorNames;
+ /**
+ * Cell bandwidth, in kHz.
+ */
+ int32_t bandwidth;
};
struct CellIdentityWcdma {
@@ -222,6 +287,10 @@
* Valid only if type = tdscdma and size = 1 else must be empty.
*/
vec<CellInfoTdscdma> tdscdma;
+ /**
+ * Connection status for the cell.
+ */
+ CellConnectionStatus connectionStatus;
};
struct CardStatus {
@@ -244,3 +313,54 @@
*/
string iccid;
};
+
+struct LinkCapacityEstimate {
+ /**
+ * Estimated downlink capacity in kbps.
+ */
+ uint32_t downlinkCapacityKbps;
+ /**
+ * Estimated uplink capacity in kbps.
+ */
+ uint32_t uplinkCapacityKbps;
+};
+
+struct PhysicalChannelConfig {
+ /**
+ * Connection status for cell. Valid values are PRIMARY_SERVING and SECONDARY_SERVING.
+ */
+ CellConnectionStatus status;
+ /**
+ * Cell bandwidth, in kHz.
+ */
+ int32_t cellBandwidthDownlink;
+};
+
+enum AccessNetwork : int32_t {
+ /** GSM EDGE Radio Access Network */
+ GERAN = 1,
+ /** Universal Terrestrial Radio Access Network */
+ UTRAN = 2,
+ /** Evolved Universal Terrestrial Radio Access Network */
+ EUTRAN = 3,
+ /** CDMA 2000 network */
+ CDMA2000 = 4,
+ /** Interworking Wireless LAN */
+ IWLAN = 5
+};
+
+enum DataRequestReason : int32_t {
+ /**
+ * The reason of the data request is normal
+ */
+ NORMAL = 0x01,
+ /**
+ * The reason of the data request is device shutdown
+ */
+ SHUTDOWN = 0x02,
+ /**
+ * The reason of the data request is IWLAN data handover to another transport
+ * (e.g. from cellular to wifi or vise versa)
+ */
+ HANDOVER = 0x03,
+};
diff --git a/radio/config/1.0/default/Android.bp b/radio/config/1.0/default/Android.bp
new file mode 100644
index 0000000..f52335e
--- /dev/null
+++ b/radio/config/1.0/default/Android.bp
@@ -0,0 +1,20 @@
+cc_binary {
+ name: "android.hardware.radio.config@1.0-service",
+ init_rc: ["android.hardware.radio.config@1.0-service.rc"],
+ relative_install_path: "hw",
+ vendor: true,
+ srcs: [
+ "RadioConfig.cpp",
+ "RadioConfigIndication.cpp",
+ "RadioConfigResponse.cpp",
+ "service.cpp",
+ ],
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ "android.hardware.radio.config@1.0",
+ "android.hardware.radio@1.0",
+ ],
+}
diff --git a/radio/config/1.0/default/RadioConfig.cpp b/radio/config/1.0/default/RadioConfig.cpp
new file mode 100644
index 0000000..af4b77e
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfig.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::config::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+Return<void> RadioConfig::setResponseFunctions(
+ const sp<IRadioConfigResponse>& radioConfigResponse,
+ const sp<IRadioConfigIndication>& radioConfigIndication) {
+ mRadioConfigResponse = radioConfigResponse;
+ mRadioConfigIndication = radioConfigIndication;
+ return Void();
+}
+
+Return<void> RadioConfig::getSimSlotsStatus(int32_t /* serial */) {
+ hidl_vec<SimSlotStatus> slotStatus;
+ ::android::hardware::radio::V1_0::RadioResponseInfo info;
+ mRadioConfigResponse->getSimSlotsStatusResponse(info, slotStatus);
+ return Void();
+}
+
+Return<void> RadioConfig::setSimSlotsMapping(int32_t /* serial */,
+ const hidl_vec<uint32_t>& /* slotMap */) {
+ ::android::hardware::radio::V1_0::RadioResponseInfo info;
+ mRadioConfigResponse->setSimSlotsMappingResponse(info);
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
diff --git a/radio/config/1.0/default/RadioConfig.h b/radio/config/1.0/default/RadioConfig.h
new file mode 100644
index 0000000..0f0ac75
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfig.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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_0_RADIOCONFIG_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIG_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+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;
+using ::android::sp;
+
+struct RadioConfig : public IRadioConfig {
+ sp<IRadioConfigResponse> mRadioConfigResponse;
+ sp<IRadioConfigIndication> mRadioConfigIndication;
+ // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+ Return<void> setResponseFunctions(
+ const sp<::android::hardware::radio::config::V1_0::IRadioConfigResponse>&
+ radioConfigResponse,
+ const sp<::android::hardware::radio::config::V1_0::IRadioConfigIndication>&
+ radioConfigIndication) override;
+ Return<void> getSimSlotsStatus(int32_t serial) override;
+ Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap) override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIG_H
diff --git a/radio/config/1.0/default/RadioConfigIndication.cpp b/radio/config/1.0/default/RadioConfigIndication.cpp
new file mode 100644
index 0000000..1005ca3
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigIndication.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
+Return<void> RadioConfigIndication::simSlotsStatusChanged(
+ RadioIndicationType /* type */, const hidl_vec<SimSlotStatus>& /* slotStatus */) {
+ // TODO implement
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
diff --git a/radio/config/1.0/default/RadioConfigIndication.h b/radio/config/1.0/default/RadioConfigIndication.h
new file mode 100644
index 0000000..cfab1e6
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigIndication.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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_0_RADIOCONFIGINDICATION_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGINDICATION_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfigIndication.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+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;
+using ::android::sp;
+
+struct RadioConfigIndication : public IRadioConfigIndication {
+ // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigIndication follow.
+ Return<void> simSlotsStatusChanged(
+ ::android::hardware::radio::V1_0::RadioIndicationType type,
+ const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
+ override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGINDICATION_H
diff --git a/radio/config/1.0/default/RadioConfigResponse.cpp b/radio/config/1.0/default/RadioConfigResponse.cpp
new file mode 100644
index 0000000..029eab2
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigResponse.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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_0 {
+namespace implementation {
+
+using namespace ::android::hardware::radio::V1_0;
+using namespace ::android::hardware::radio::config::V1_0;
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+ const RadioResponseInfo& /* info */, const hidl_vec<SimSlotStatus>& /* slotStatus */) {
+ // TODO implement
+ return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(const RadioResponseInfo& /* info */) {
+ // TODO implement
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
diff --git a/radio/config/1.0/default/RadioConfigResponse.h b/radio/config/1.0/default/RadioConfigResponse.h
new file mode 100644
index 0000000..7d121fd
--- /dev/null
+++ b/radio/config/1.0/default/RadioConfigResponse.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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_0_RADIOCONFIGRESPONSE_H
+#define ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGRESPONSE_H
+
+#include <android/hardware/radio/config/1.0/IRadioConfigResponse.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace radio {
+namespace config {
+namespace V1_0 {
+namespace implementation {
+
+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;
+using ::android::sp;
+
+struct RadioConfigResponse : public IRadioConfigResponse {
+ // Methods from ::android::hardware::radio::config::V1_0::IRadioConfigResponse follow.
+ Return<void> getSimSlotsStatusResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+ const hidl_vec<::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus)
+ override;
+ Return<void> setSimSlotsMappingResponse(
+ const ::android::hardware::radio::V1_0::RadioResponseInfo& info) override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace config
+} // namespace radio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_RADIO_CONFIG_V1_0_RADIOCONFIGRESPONSE_H
diff --git a/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc b/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc
new file mode 100644
index 0000000..fad16b1
--- /dev/null
+++ b/radio/config/1.0/default/android.hardware.radio.config@1.0-service.rc
@@ -0,0 +1,4 @@
+service vendor.radio-config-hal-1-0 /vendor/bin/hw/android.hardware.radio.config@1.0-service
+ class hal
+ user system
+ group system
diff --git a/radio/config/1.0/default/service.cpp b/radio/config/1.0/default/service.cpp
new file mode 100644
index 0000000..a06cc22
--- /dev/null
+++ b/radio/config/1.0/default/service.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.0-service"
+
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "RadioConfig.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::radio::config::V1_0::IRadioConfig;
+using android::hardware::radio::config::V1_0::implementation::RadioConfig;
+using android::sp;
+using android::status_t;
+using android::OK;
+
+int main() {
+ configureRpcThreadpool(1, true);
+
+ sp<IRadioConfig> radioConfig = new RadioConfig;
+ status_t status = radioConfig->registerAsService();
+ ALOGW_IF(status != OK, "Could not register IRadioConfig");
+ ALOGD("Default service is ready.");
+
+ joinRpcThreadpool();
+ return 0;
+}
\ No newline at end of file
diff --git a/renderscript/1.0/default/Android.bp b/renderscript/1.0/default/Android.bp
index b996969..d5d6d8d 100644
--- a/renderscript/1.0/default/Android.bp
+++ b/renderscript/1.0/default/Android.bp
@@ -12,7 +12,7 @@
],
shared_libs: [
"libdl",
- "liblog",
+ "libbase",
"libhidlbase",
"libhidltransport",
"libutils",
diff --git a/renderscript/1.0/default/Context.cpp b/renderscript/1.0/default/Context.cpp
index fbfc652..f5b70c9 100644
--- a/renderscript/1.0/default/Context.cpp
+++ b/renderscript/1.0/default/Context.cpp
@@ -1,5 +1,3 @@
-#define LOG_TAG "android.hardware.renderscript@1.0-impl"
-
#include "Context.h"
#include "Device.h"
diff --git a/renderscript/1.0/default/Device.cpp b/renderscript/1.0/default/Device.cpp
index a2b950d..8fda3ff 100644
--- a/renderscript/1.0/default/Device.cpp
+++ b/renderscript/1.0/default/Device.cpp
@@ -1,6 +1,7 @@
#include "Context.h"
#include "Device.h"
+#include <android-base/logging.h>
#include <android/dlext.h>
#include <dlfcn.h>
@@ -54,12 +55,18 @@
.flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = rsNamespace,
};
handle = android_dlopen_ext(filename, RTLD_LAZY | RTLD_LOCAL, &dlextinfo);
+ if (handle == nullptr) {
+ LOG(WARNING) << "android_dlopen_ext(" << filename << ") failed: " << dlerror();
+ }
}
}
if (handle == nullptr) {
// if there is no "rs" namespace (in case when this HAL impl is loaded
// into a vendor process), then use the plain dlopen.
handle = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);
+ if (handle == nullptr) {
+ LOG(FATAL) << "dlopen(" << filename << ") failed: " << dlerror();
+ }
}
dispatchTable dispatchHal = {
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index ca3d3e4..24b58a7 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -15,8 +15,8 @@
*/
#define LOG_TAG "sensors_hidl_hal_test"
-#include "GrallocWrapper.h"
#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
#include <android-base/logging.h>
#include <android/hardware/sensors/1.0/ISensors.h>
#include <android/hardware/sensors/1.0/types.h>
@@ -24,6 +24,7 @@
#include <hardware/sensors.h> // for sensor type strings
#include <log/log.h>
#include <utils/SystemClock.h>
+#include "GrallocWrapper.h"
#include <algorithm>
#include <cinttypes>
@@ -46,63 +47,65 @@
// Test environment for sensors
class SensorsHidlTest;
-class SensorsHidlEnvironment : public ::testing::Environment {
- public:
- // get the test environment singleton
- static SensorsHidlEnvironment* Instance() {
- static SensorsHidlEnvironment* instance = new SensorsHidlEnvironment;
- return instance;
- }
+class SensorsHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static SensorsHidlEnvironment* Instance() {
+ static SensorsHidlEnvironment* instance = new SensorsHidlEnvironment;
+ return instance;
+ }
- virtual void SetUp();
- virtual void TearDown();
+ virtual void HidlSetUp() override;
+ virtual void HidlTearDown() override;
- // Get and clear all events collected so far (like "cat" shell command).
- // If output is nullptr, it clears all collected events.
- void catEvents(std::vector<Event>* output);
+ virtual void registerTestServices() override { registerTestService<ISensors>(); }
- // set sensor event collection status
- void setCollection(bool enable);
+ // Get and clear all events collected so far (like "cat" shell command).
+ // If output is nullptr, it clears all collected events.
+ void catEvents(std::vector<Event>* output);
- private:
- friend SensorsHidlTest;
- // sensors hidl service
- sp<ISensors> sensors;
+ // set sensor event collection status
+ void setCollection(bool enable);
- SensorsHidlEnvironment() {}
+ private:
+ friend SensorsHidlTest;
+ // sensors hidl service
+ sp<ISensors> sensors;
- void addEvent(const Event& ev);
- void startPollingThread();
- void resetHal();
- static void pollingThread(SensorsHidlEnvironment* env, std::shared_ptr<bool> stop);
+ SensorsHidlEnvironment() {}
- bool collectionEnabled;
- std::shared_ptr<bool> stopThread;
- std::thread pollThread;
- std::vector<Event> events;
- std::mutex events_mutex;
+ void addEvent(const Event& ev);
+ void startPollingThread();
+ void resetHal();
+ static void pollingThread(SensorsHidlEnvironment* env, std::shared_ptr<bool> stop);
- GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironment);
+ bool collectionEnabled;
+ std::shared_ptr<bool> stopThread;
+ std::thread pollThread;
+ std::vector<Event> events;
+ std::mutex events_mutex;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironment);
};
-void SensorsHidlEnvironment::SetUp() {
- resetHal();
+void SensorsHidlEnvironment::HidlSetUp() {
+ resetHal();
- ASSERT_NE(sensors, nullptr) << "sensors is nullptr, cannot get hidl service";
+ ASSERT_NE(sensors, nullptr) << "sensors is nullptr, cannot get hidl service";
- collectionEnabled = false;
- startPollingThread();
+ collectionEnabled = false;
+ startPollingThread();
- // In case framework just stopped for test and there is sensor events in the pipe,
- // wait some time for those events to be cleared to avoid them messing up the test.
- std::this_thread::sleep_for(std::chrono::seconds(3));
+ // In case framework just stopped for test and there is sensor events in the pipe,
+ // wait some time for those events to be cleared to avoid them messing up the test.
+ std::this_thread::sleep_for(std::chrono::seconds(3));
}
-void SensorsHidlEnvironment::TearDown() {
- if (stopThread) {
- *stopThread = true;
- }
- pollThread.detach();
+void SensorsHidlEnvironment::HidlTearDown() {
+ if (stopThread) {
+ *stopThread = true;
+ }
+ pollThread.detach();
}
void SensorsHidlEnvironment::resetHal() {
@@ -115,7 +118,8 @@
// this do ... while is for easy error handling
do {
step = "getService()";
- sensors = ISensors::getService();
+ sensors = ISensors::getService(
+ SensorsHidlEnvironment::Instance()->getServiceName<ISensors>());
if (sensors == nullptr) {
break;
}
@@ -1500,6 +1504,7 @@
int main(int argc, char **argv) {
::testing::AddGlobalTestEnvironment(SensorsHidlEnvironment::Instance());
::testing::InitGoogleTest(&argc, argv);
+ SensorsHidlEnvironment::Instance()->init(&argc, argv);
int status = RUN_ALL_TESTS();
ALOGI("Test result = %d", status);
return status;
diff --git a/vibrator/1.2/Android.bp b/vibrator/1.2/Android.bp
new file mode 100644
index 0000000..88192c1
--- /dev/null
+++ b/vibrator/1.2/Android.bp
@@ -0,0 +1,23 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.vibrator@1.2",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "IVibrator.hal",
+ ],
+ interfaces: [
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hidl.base@1.0",
+ ],
+ types: [
+ "Effect",
+ ],
+ gen_java: true,
+}
+
diff --git a/vibrator/1.2/IVibrator.hal b/vibrator/1.2/IVibrator.hal
new file mode 100644
index 0000000..7244da1
--- /dev/null
+++ b/vibrator/1.2/IVibrator.hal
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator@1.2;
+
+import @1.0::EffectStrength;
+import @1.0::Status;
+import @1.1::IVibrator;
+
+interface IVibrator extends @1.1::IVibrator {
+ /**
+ * Fire off a predefined haptic event.
+ *
+ * @param event The type of haptic event to trigger.
+ * @return status Whether the effect was successfully performed or not. Must
+ * return Status::UNSUPPORTED_OPERATION is the effect is not supported.
+ * @return lengthMs The length of time the event is expected to take in
+ * milliseconds. This doesn't need to be perfectly accurate, but should be a reasonable
+ * approximation. Should be a positive, non-zero value if the returned status is Status::OK,
+ * and set to 0 otherwise.
+ */
+ perform_1_2(Effect effect, EffectStrength strength)
+ generates (Status status, uint32_t lengthMs);
+};
diff --git a/vibrator/1.2/types.hal b/vibrator/1.2/types.hal
new file mode 100644
index 0000000..7604f2c
--- /dev/null
+++ b/vibrator/1.2/types.hal
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.vibrator@1.2;
+
+import @1.1::Effect_1_1;
+
+// Note that while the previous type had a version suffix, this type does not. This is because the
+// versions are already present in the namespace and thus don't need to also be embedded in the
+// name of the type.
+enum Effect : @1.1::Effect_1_1 {
+ /**
+ * A thud effect.
+ *
+ * This effect should solid feeling bump, like the depression of a heavy mechanical button.
+ */
+ THUD,
+ /**
+ * A pop effect.
+ *
+ * A short, quick burst effect.
+ */
+ POP,
+
+ /**
+ * A heavy click effect.
+ *
+ * This should produce a sharp striking sensation, like a click but stronger.
+ */
+ HEAVY_CLICK,
+
+ /**
+ * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
+ * pattern that can be played as a ringtone with any audio, depending on the device.
+ */
+ RINGTONE_1,
+ RINGTONE_2,
+ RINGTONE_3,
+ RINGTONE_4,
+ RINGTONE_5,
+ RINGTONE_6,
+ RINGTONE_7,
+ RINGTONE_8,
+ RINGTONE_9,
+ RINGTONE_10,
+ RINGTONE_11,
+ RINGTONE_12,
+ RINGTONE_13,
+ RINGTONE_14,
+ RINGTONE_15,
+};
diff --git a/vibrator/1.2/vts/functional/Android.bp b/vibrator/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..3a4e2ef
--- /dev/null
+++ b/vibrator/1.2/vts/functional/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalVibratorV1_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["VtsHalVibratorV1_2TargetTest.cpp"],
+ static_libs: [
+ "android.hardware.vibrator@1.0",
+ "android.hardware.vibrator@1.1",
+ "android.hardware.vibrator@1.2",
+ ],
+}
+
diff --git a/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp b/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp
new file mode 100644
index 0000000..d07d1b7
--- /dev/null
+++ b/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "vibrator_hidl_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/vibrator/1.0/types.h>
+#include <android/hardware/vibrator/1.2/IVibrator.h>
+#include <android/hardware/vibrator/1.2/types.h>
+#include <unistd.h>
+
+using ::android::hardware::vibrator::V1_0::Status;
+using ::android::hardware::vibrator::V1_0::EffectStrength;
+using ::android::hardware::vibrator::V1_2::Effect;
+using ::android::hardware::vibrator::V1_2::IVibrator;
+using ::android::hardware::hidl_enum_iterator;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+// The main test class for VIBRATOR HIDL HAL 1.2.
+class VibratorHidlTest_1_2 : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ vibrator = ::testing::VtsHalHidlTargetTestBase::getService<IVibrator>();
+ ASSERT_NE(vibrator, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ sp<IVibrator> vibrator;
+};
+
+static void validatePerformEffect(Status status, uint32_t lengthMs) {
+ ASSERT_TRUE(status == Status::OK || status == Status::UNSUPPORTED_OPERATION);
+ if (status == Status::OK) {
+ ASSERT_GT(lengthMs, static_cast<uint32_t>(0))
+ << "Effects that return OK must return a non-zero duration";
+ } else {
+ ASSERT_EQ(lengthMs, static_cast<uint32_t>(0))
+ << "Effects that return UNSUPPORTED_OPERATION must have a duration of zero";
+ }
+}
+
+TEST_F(VibratorHidlTest_1_2, PerformEffect_1_2) {
+ for (const auto& effect : hidl_enum_iterator<Effect>()) {
+ for (const auto& strength : hidl_enum_iterator<EffectStrength>()) {
+ vibrator->perform_1_2(effect, strength, validatePerformEffect);
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/wifi/1.2/Android.bp b/wifi/1.2/Android.bp
index 100b36b..f41324e 100644
--- a/wifi/1.2/Android.bp
+++ b/wifi/1.2/Android.bp
@@ -10,6 +10,7 @@
"types.hal",
"IWifi.hal",
"IWifiChip.hal",
+ "IWifiChipEventCallback.hal",
"IWifiNanIface.hal",
"IWifiNanIfaceEventCallback.hal",
],
diff --git a/wifi/1.2/IWifiChip.hal b/wifi/1.2/IWifiChip.hal
index 72cbf81..480c5a2 100644
--- a/wifi/1.2/IWifiChip.hal
+++ b/wifi/1.2/IWifiChip.hal
@@ -16,7 +16,9 @@
package android.hardware.wifi@1.2;
+import @1.0::WifiStatus;
import @1.1::IWifiChip;
+import IWifiChipEventCallback;
/**
* Interface that represents a chip that must be configured as a single unit.
@@ -24,4 +26,62 @@
* to perform operations like NAN, RTT, etc.
*/
interface IWifiChip extends @1.1::IWifiChip {
+ /**
+ * Capabilities exposed by this chip.
+ */
+ enum ChipCapabilityMask : @1.1::IWifiChip.ChipCapabilityMask {
+ /**
+ * Set/Reset Tx Power limits.
+ */
+ USE_BODY_HEAD_SAR = 1 << 11
+ };
+
+ /**
+ * List of preset wifi radio TX power levels for different scenarios.
+ * The actual power values (typically varies based on the channel,
+ * 802.11 connection type, number of MIMO streams, etc) for each scenario
+ * is defined by the OEM as a BDF file since it varies for each wifi chip
+ * vendor and device.
+ */
+ enum TxPowerScenario : @1.1::IWifiChip.TxPowerScenario {
+ ON_HEAD_CELL_OFF = 1,
+ ON_HEAD_CELL_ON = 2,
+ ON_BODY_CELL_OFF = 3,
+ ON_BODY_CELL_ON = 4
+ };
+
+ /**
+ * API to select one of the preset TX power scenarios.
+ *
+ * The framework must invoke this method with the appropriate scenario to let
+ * the wifi chip change it's transmitting power levels.
+ * OEM's should define various power profiles for each of the scenarios
+ * above (defined in |TxPowerScenario|) in a vendor extension.
+ *
+ * @param scenario One of the preselected scenarios defined in
+ * |TxPowerScenario|.
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|,
+ * |WifiStatusCode.ERROR_NOT_SUPPORTED|,
+ * |WifiStatusCode.NOT_AVAILABLE|,
+ * |WifiStatusCode.UNKNOWN|
+ */
+ selectTxPowerScenario_1_2(TxPowerScenario scenario) generates (WifiStatus status);
+
+ /**
+ * Requests notifications of significant events on this chip. Multiple calls
+ * to this must register multiple callbacks each of which must receive all
+ * events.
+ *
+ * @param callback An instance of the |IWifiChipEventCallback| HIDL interface
+ * object.
+ * @return status WifiStatus of the operation.
+ * Possible status codes:
+ * |WifiStatusCode.SUCCESS|,
+ * |WifiStatusCode.ERROR_WIFI_CHIP_INVALID|
+ */
+ registerEventCallback_1_2(IWifiChipEventCallback callback)
+ generates (WifiStatus status);
};
diff --git a/wifi/1.2/IWifiChipEventCallback.hal b/wifi/1.2/IWifiChipEventCallback.hal
new file mode 100644
index 0000000..5d2e7e9
--- /dev/null
+++ b/wifi/1.2/IWifiChipEventCallback.hal
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi@1.2;
+
+import @1.0::IWifiChipEventCallback;
+import @1.0::WifiBand;
+
+/**
+ * Wifi chip event callbacks.
+ */
+interface IWifiChipEventCallback extends @1.0::IWifiChipEventCallback {
+ /**
+ * Struct describing the state of each iface operating on the radio chain
+ * (hardware MAC) on the device.
+ */
+ struct IfaceInfo {
+ /** Name of the interface (For ex: "wlan0"). */
+ string name;
+ /** Wifi channel on which this interface is operating. */
+ uint32_t channel;
+ };
+
+ /**
+ * Struct describing the state of each hardware radio chain (hardware MAC)
+ * on the device.
+ */
+ struct RadioModeInfo {
+ /**
+ * Identifier for this radio chain. This is vendor dependent & used
+ * only for debugging purposes.
+ */
+ uint32_t radioId;
+ /**
+ * List of bands on which this radio chain is operating.
+ * Can be one of:
+ * a) WifiBand.BAND_24GHZ => 2.4Ghz.
+ * b) WifiBand.BAND_5GHZ => 5Ghz.
+ * c) WifiBand.BAND_24GHZ_5GHZ = 2.4Ghz + 5Ghz (Radio is time sharing
+ * across the 2 bands).
+ */
+ WifiBand bandInfo;
+ /** List of interfaces on this radio chain (hardware MAC). */
+ vec<IfaceInfo> ifaceInfos;
+ };
+
+ /**
+ * Asynchronous callback indicating a radio mode change.
+ * Radio mode change could be a result of:
+ * a) Bringing up concurrent interfaces (For ex: STA + AP).
+ * b) Change in operating band of one of the concurrent interfaces (For ex:
+ * STA connection moved from 2.4G to 5G)
+ *
+ * @param radioModeInfos List of RadioModeInfo structures for each
+ * radio chain (hardware MAC) on the device.
+ */
+ oneway onRadioModeChange(vec<RadioModeInfo> radioModeInfos);
+};
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index 95414bc..978cf63 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -30,6 +30,7 @@
LOCAL_SRC_FILES := \
hidl_struct_util.cpp \
hidl_sync_util.cpp \
+ ringbuffer.cpp \
wifi.cpp \
wifi_ap_iface.cpp \
wifi_chip.cpp \
@@ -93,10 +94,12 @@
LOCAL_MODULE := android.hardware.wifi@1.0-service-tests
LOCAL_PROPRIETARY_MODULE := true
LOCAL_SRC_FILES := \
+ tests/hidl_struct_util_unit_tests.cpp \
tests/main.cpp \
tests/mock_wifi_feature_flags.cpp \
tests/mock_wifi_legacy_hal.cpp \
tests/mock_wifi_mode_controller.cpp \
+ tests/ringbuffer_unit_tests.cpp \
tests/wifi_chip_unit_tests.cpp
LOCAL_STATIC_LIBRARIES := \
libgmock \
diff --git a/wifi/1.2/default/hidl_struct_util.cpp b/wifi/1.2/default/hidl_struct_util.cpp
index f87828c..2e3e0ab 100644
--- a/wifi/1.2/default/hidl_struct_util.cpp
+++ b/wifi/1.2/default/hidl_struct_util.cpp
@@ -66,12 +66,14 @@
return {};
}
-V1_1::IWifiChip::ChipCapabilityMask convertLegacyFeatureToHidlChipCapability(
+IWifiChip::ChipCapabilityMask convertLegacyFeatureToHidlChipCapability(
uint32_t feature) {
- using HidlChipCaps = V1_1::IWifiChip::ChipCapabilityMask;
+ using HidlChipCaps = IWifiChip::ChipCapabilityMask;
switch (feature) {
case WIFI_FEATURE_SET_TX_POWER_LIMIT:
return HidlChipCaps::SET_TX_POWER_LIMIT;
+ case WIFI_FEATURE_USE_BODY_HEAD_SAR:
+ return HidlChipCaps::USE_BODY_HEAD_SAR;
case WIFI_FEATURE_D2D_RTT:
return HidlChipCaps::D2D_RTT;
case WIFI_FEATURE_D2AP_RTT:
@@ -135,6 +137,7 @@
}
}
for (const auto feature : {WIFI_FEATURE_SET_TX_POWER_LIMIT,
+ WIFI_FEATURE_USE_BODY_HEAD_SAR,
WIFI_FEATURE_D2D_RTT, WIFI_FEATURE_D2AP_RTT}) {
if (feature & legacy_feature_set) {
*hidl_caps |= convertLegacyFeatureToHidlChipCapability(feature);
@@ -260,12 +263,83 @@
legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy(
V1_1::IWifiChip::TxPowerScenario hidl_scenario) {
switch (hidl_scenario) {
- case V1_1::IWifiChip::TxPowerScenario::VOICE_CALL:
+ // This is the only supported scenario for V1_1
+ case V1_1::IWifiChip::TxPowerScenario::VOICE_CALL:
return legacy_hal::WIFI_POWER_SCENARIO_VOICE_CALL;
};
CHECK(false);
}
+legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy_1_2(
+ IWifiChip::TxPowerScenario hidl_scenario) {
+ switch (hidl_scenario) {
+ // This is the only supported scenario for V1_1
+ case IWifiChip::TxPowerScenario::VOICE_CALL:
+ return legacy_hal::WIFI_POWER_SCENARIO_VOICE_CALL;
+ // Those are the supported scenarios for V1_2
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_HEAD_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_OFF:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+ case IWifiChip::TxPowerScenario::ON_BODY_CELL_ON:
+ return legacy_hal::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+ };
+ CHECK(false);
+}
+
+bool convertLegacyWifiMacInfoToHidl(
+ const legacy_hal::WifiMacInfo& legacy_mac_info,
+ IWifiChipEventCallback::RadioModeInfo* hidl_radio_mode_info) {
+ if (!hidl_radio_mode_info) {
+ return false;
+ }
+ *hidl_radio_mode_info = {};
+
+ hidl_radio_mode_info->radioId = legacy_mac_info.wlan_mac_id;
+ // Convert from bitmask of bands in the legacy HAL to enum value in
+ // the HIDL interface.
+ if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND &&
+ legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+ hidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ_5GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_2_4_BAND) {
+ hidl_radio_mode_info->bandInfo = WifiBand::BAND_24GHZ;
+ } else if (legacy_mac_info.mac_band & legacy_hal::WLAN_MAC_5_0_BAND) {
+ hidl_radio_mode_info->bandInfo = WifiBand::BAND_5GHZ;
+ } else {
+ hidl_radio_mode_info->bandInfo = WifiBand::BAND_UNSPECIFIED;
+ }
+ std::vector<IWifiChipEventCallback::IfaceInfo> iface_info_vec;
+ for (const auto& legacy_iface_info : legacy_mac_info.iface_infos) {
+ IWifiChipEventCallback::IfaceInfo iface_info;
+ iface_info.name = legacy_iface_info.name;
+ iface_info.channel = legacy_iface_info.channel;
+ iface_info_vec.push_back(iface_info);
+ }
+ hidl_radio_mode_info->ifaceInfos = iface_info_vec;
+ return true;
+}
+
+bool convertLegacyWifiMacInfosToHidl(
+ const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+ std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos) {
+ if (!hidl_radio_mode_infos) {
+ return false;
+ }
+ *hidl_radio_mode_infos = {};
+
+ for (const auto& legacy_mac_info : legacy_mac_infos) {
+ IWifiChipEventCallback::RadioModeInfo hidl_radio_mode_info;
+ if (!convertLegacyWifiMacInfoToHidl(legacy_mac_info,
+ &hidl_radio_mode_info)) {
+ return false;
+ }
+ hidl_radio_mode_infos->push_back(hidl_radio_mode_info);
+ }
+ return true;
+}
+
bool convertLegacyFeaturesToHidlStaCapabilities(
uint32_t legacy_feature_set, uint32_t legacy_logger_feature_set,
uint32_t* hidl_caps) {
diff --git a/wifi/1.2/default/hidl_struct_util.h b/wifi/1.2/default/hidl_struct_util.h
index 1208afd..3c789c0 100644
--- a/wifi/1.2/default/hidl_struct_util.h
+++ b/wifi/1.2/default/hidl_struct_util.h
@@ -21,7 +21,8 @@
#include <android/hardware/wifi/1.0/IWifiChip.h>
#include <android/hardware/wifi/1.0/types.h>
-#include <android/hardware/wifi/1.1/IWifiChip.h>
+#include <android/hardware/wifi/1.2/IWifiChip.h>
+#include <android/hardware/wifi/1.2/IWifiChipEventCallback.h>
#include <android/hardware/wifi/1.2/types.h>
#include "wifi_legacy_hal.h"
@@ -55,6 +56,11 @@
WifiDebugHostWakeReasonStats* hidl_stats);
legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy(
V1_1::IWifiChip::TxPowerScenario hidl_scenario);
+legacy_hal::wifi_power_scenario convertHidlTxPowerScenarioToLegacy_1_2(
+ IWifiChip::TxPowerScenario hidl_scenario);
+bool convertLegacyWifiMacInfosToHidl(
+ const std::vector<legacy_hal::WifiMacInfo>& legacy_mac_infos,
+ std::vector<IWifiChipEventCallback::RadioModeInfo>* hidl_radio_mode_infos);
// STA iface conversion methods.
bool convertLegacyFeaturesToHidlStaCapabilities(
diff --git a/wifi/1.2/default/ringbuffer.cpp b/wifi/1.2/default/ringbuffer.cpp
new file mode 100644
index 0000000..5511f2f
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ringbuffer.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+Ringbuffer::Ringbuffer(size_t maxSize) : size_(0), maxSize_(maxSize) {}
+
+void Ringbuffer::append(const std::vector<uint8_t>& input) {
+ if (input.size() == 0) {
+ return;
+ }
+ data_.push_back(input);
+ size_ += input.size() * sizeof(input[0]);
+ while (size_ > maxSize_) {
+ size_ -= data_.front().size() * sizeof(data_.front()[0]);
+ data_.pop_front();
+ }
+}
+
+const std::list<std::vector<uint8_t>>& Ringbuffer::getData() const {
+ return data_;
+}
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace wifi
+} // namespace hardware
+} // namespace android
diff --git a/wifi/1.2/default/ringbuffer.h b/wifi/1.2/default/ringbuffer.h
new file mode 100644
index 0000000..4808e40
--- /dev/null
+++ b/wifi/1.2/default/ringbuffer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RINGBUFFER_H_
+#define RINGBUFFER_H_
+
+#include <list>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+/**
+ * Ringbuffer object used to store debug data.
+ */
+class Ringbuffer {
+ public:
+ explicit Ringbuffer(size_t maxSize);
+
+ // Appends the data buffer and deletes from the front until buffer is
+ // within |maxSize_|.
+ void append(const std::vector<uint8_t>& input);
+ const std::list<std::vector<uint8_t>>& getData() const;
+
+ private:
+ std::list<std::vector<uint8_t>> data_;
+ size_t size_;
+ size_t maxSize_;
+};
+
+} // namespace implementation
+} // namespace V1_2
+} // namespace wifi
+} // namespace hardware
+} // namespace android
+
+#endif // RINGBUFFER_H_
diff --git a/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp b/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp
new file mode 100644
index 0000000..1d6e9e4
--- /dev/null
+++ b/wifi/1.2/default/tests/hidl_struct_util_unit_tests.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN
+#include "hidl_struct_util.h"
+
+using testing::Test;
+
+namespace {
+constexpr uint32_t kMacId1 = 1;
+constexpr uint32_t kMacId2 = 2;
+constexpr uint32_t kIfaceChannel1 = 3;
+constexpr uint32_t kIfaceChannel2 = 5;
+constexpr char kIfaceName1[] = "wlan0";
+constexpr char kIfaceName2[] = "wlan1";
+} // namespace
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+using namespace android::hardware::wifi::V1_0;
+
+class HidlStructUtilTest : public Test {};
+
+TEST_F(HidlStructUtilTest, CanConvertLegacyWifiMacInfosToHidlWithOneMac) {
+ std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+ legacy_hal::WifiMacInfo legacy_mac_info1 = {
+ .wlan_mac_id = kMacId1,
+ .mac_band =
+ legacy_hal::WLAN_MAC_5_0_BAND | legacy_hal::WLAN_MAC_2_4_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1,
+ .channel = kIfaceChannel1};
+ legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2,
+ .channel = kIfaceChannel2};
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info2);
+ legacy_mac_infos.push_back(legacy_mac_info1);
+
+ std::vector<IWifiChipEventCallback::RadioModeInfo> hidl_radio_mode_infos;
+ ASSERT_TRUE(hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+ legacy_mac_infos, &hidl_radio_mode_infos));
+
+ ASSERT_EQ(1u, hidl_radio_mode_infos.size());
+ auto hidl_radio_mode_info1 = hidl_radio_mode_infos[0];
+ EXPECT_EQ(legacy_mac_info1.wlan_mac_id, hidl_radio_mode_info1.radioId);
+ EXPECT_EQ(WifiBand::BAND_24GHZ_5GHZ, hidl_radio_mode_info1.bandInfo);
+ ASSERT_EQ(2u, hidl_radio_mode_info1.ifaceInfos.size());
+ auto hidl_iface_info1 = hidl_radio_mode_info1.ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info1.name, hidl_iface_info1.name);
+ EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info1.channel),
+ hidl_iface_info1.channel);
+ auto hidl_iface_info2 = hidl_radio_mode_info1.ifaceInfos[1];
+ EXPECT_EQ(legacy_iface_info2.name, hidl_iface_info2.name);
+ EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info2.channel),
+ hidl_iface_info2.channel);
+}
+
+TEST_F(HidlStructUtilTest, CanConvertLegacyWifiMacInfosToHidlWithTwoMac) {
+ std::vector<legacy_hal::WifiMacInfo> legacy_mac_infos;
+ legacy_hal::WifiMacInfo legacy_mac_info1 = {
+ .wlan_mac_id = kMacId1, .mac_band = legacy_hal::WLAN_MAC_5_0_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info1 = {.name = kIfaceName1,
+ .channel = kIfaceChannel1};
+ legacy_hal::WifiMacInfo legacy_mac_info2 = {
+ .wlan_mac_id = kMacId2, .mac_band = legacy_hal::WLAN_MAC_2_4_BAND};
+ legacy_hal::WifiIfaceInfo legacy_iface_info2 = {.name = kIfaceName2,
+ .channel = kIfaceChannel2};
+ legacy_mac_info1.iface_infos.push_back(legacy_iface_info1);
+ legacy_mac_infos.push_back(legacy_mac_info1);
+ legacy_mac_info2.iface_infos.push_back(legacy_iface_info2);
+ legacy_mac_infos.push_back(legacy_mac_info2);
+
+ std::vector<IWifiChipEventCallback::RadioModeInfo> hidl_radio_mode_infos;
+ ASSERT_TRUE(hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+ legacy_mac_infos, &hidl_radio_mode_infos));
+
+ ASSERT_EQ(2u, hidl_radio_mode_infos.size());
+
+ // Find mac info 1.
+ const auto hidl_radio_mode_info1 = std::find_if(
+ hidl_radio_mode_infos.begin(), hidl_radio_mode_infos.end(),
+ [&legacy_mac_info1](const IWifiChipEventCallback::RadioModeInfo& x) {
+ return x.radioId == legacy_mac_info1.wlan_mac_id;
+ });
+ ASSERT_NE(hidl_radio_mode_infos.end(), hidl_radio_mode_info1);
+ EXPECT_EQ(WifiBand::BAND_5GHZ, hidl_radio_mode_info1->bandInfo);
+ ASSERT_EQ(1u, hidl_radio_mode_info1->ifaceInfos.size());
+ auto hidl_iface_info1 = hidl_radio_mode_info1->ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info1.name, hidl_iface_info1.name);
+ EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info1.channel),
+ hidl_iface_info1.channel);
+
+ // Find mac info 2.
+ const auto hidl_radio_mode_info2 = std::find_if(
+ hidl_radio_mode_infos.begin(), hidl_radio_mode_infos.end(),
+ [&legacy_mac_info2](const IWifiChipEventCallback::RadioModeInfo& x) {
+ return x.radioId == legacy_mac_info2.wlan_mac_id;
+ });
+ ASSERT_NE(hidl_radio_mode_infos.end(), hidl_radio_mode_info2);
+ EXPECT_EQ(WifiBand::BAND_24GHZ, hidl_radio_mode_info2->bandInfo);
+ ASSERT_EQ(1u, hidl_radio_mode_info2->ifaceInfos.size());
+ auto hidl_iface_info2 = hidl_radio_mode_info2->ifaceInfos[0];
+ EXPECT_EQ(legacy_iface_info2.name, hidl_iface_info2.name);
+ EXPECT_EQ(static_cast<uint32_t>(legacy_iface_info2.channel),
+ hidl_iface_info2.channel);
+}
+} // namespace implementation
+} // namespace V1_2
+} // namespace wifi
+} // namespace hardware
+} // namespace android
diff --git a/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
new file mode 100644
index 0000000..1b332f9
--- /dev/null
+++ b/wifi/1.2/default/tests/ringbuffer_unit_tests.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include "ringbuffer.h"
+
+using testing::Return;
+using testing::Test;
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+class RingbufferTest : public Test {
+ public:
+ const uint32_t maxBufferSize_ = 10;
+ Ringbuffer buffer_{maxBufferSize_};
+};
+
+TEST_F(RingbufferTest, CreateEmptyBuffer) {
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, CanUseFullBufferCapacity) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ buffer_.append(input);
+ buffer_.append(input2);
+ ASSERT_EQ(2u, buffer_.getData().size());
+ EXPECT_EQ(input, buffer_.getData().front());
+ EXPECT_EQ(input2, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, OldDataIsRemovedOnOverflow) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ const std::vector<uint8_t> input3 = {'G'};
+ buffer_.append(input);
+ buffer_.append(input2);
+ buffer_.append(input3);
+ ASSERT_EQ(2u, buffer_.getData().size());
+ EXPECT_EQ(input2, buffer_.getData().front());
+ EXPECT_EQ(input3, buffer_.getData().back());
+}
+
+TEST_F(RingbufferTest, MultipleOldDataIsRemovedOnOverflow) {
+ const std::vector<uint8_t> input(maxBufferSize_ / 2, '0');
+ const std::vector<uint8_t> input2(maxBufferSize_ / 2, '1');
+ const std::vector<uint8_t> input3(maxBufferSize_, '2');
+ buffer_.append(input);
+ buffer_.append(input2);
+ buffer_.append(input3);
+ ASSERT_EQ(1u, buffer_.getData().size());
+ EXPECT_EQ(input3, buffer_.getData().front());
+}
+
+TEST_F(RingbufferTest, AppendingEmptyBufferDoesNotAddGarbage) {
+ const std::vector<uint8_t> input = {};
+ buffer_.append(input);
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+
+TEST_F(RingbufferTest, OversizedAppendIsDropped) {
+ const std::vector<uint8_t> input(maxBufferSize_ + 1, '0');
+ buffer_.append(input);
+ ASSERT_TRUE(buffer_.getData().empty());
+}
+} // namespace implementation
+} // namespace V1_2
+} // namespace wifi
+} // namespace hardware
+} // namespace android
diff --git a/wifi/1.2/default/wifi.cpp b/wifi/1.2/default/wifi.cpp
index 06f5058..79f921f 100644
--- a/wifi/1.2/default/wifi.cpp
+++ b/wifi/1.2/default/wifi.cpp
@@ -77,6 +77,15 @@
&Wifi::getChipInternal, hidl_status_cb, chip_id);
}
+Return<void> Wifi::debug(const hidl_handle& handle,
+ const hidl_vec<hidl_string>&) {
+ LOG(INFO) << "-----------Debug is called----------------";
+ if (!chip_.get()) {
+ return Void();
+ }
+ return chip_->debug(handle, {});
+}
+
WifiStatus Wifi::registerEventCallbackInternal(
const sp<IWifiEventCallback>& event_callback) {
if (!event_cb_handler_.addCallback(event_callback)) {
diff --git a/wifi/1.2/default/wifi.h b/wifi/1.2/default/wifi.h
index 440c3c7..86919b1 100644
--- a/wifi/1.2/default/wifi.h
+++ b/wifi/1.2/default/wifi.h
@@ -56,6 +56,8 @@
Return<void> stop(stop_cb hidl_status_cb) override;
Return<void> getChipIds(getChipIds_cb hidl_status_cb) override;
Return<void> getChip(ChipId chip_id, getChip_cb hidl_status_cb) override;
+ Return<void> debug(const hidl_handle& handle,
+ const hidl_vec<hidl_string>& options) override;
private:
enum class RunState { STOPPED, STARTED, STOPPING };
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 8d9cfc6..05ea638 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -14,8 +14,13 @@
* limitations under the License.
*/
+#include <fcntl.h>
+
#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <cutils/properties.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include "hidl_return_util.h"
#include "hidl_struct_util.h"
@@ -23,6 +28,7 @@
#include "wifi_status_util.h"
namespace {
+using android::base::unique_fd;
using android::hardware::hidl_string;
using android::hardware::hidl_vec;
using android::hardware::wifi::V1_0::ChipModeId;
@@ -39,6 +45,11 @@
// Mode ID for V2
constexpr ChipModeId kV2ChipModeId = 2;
+constexpr char kCpioMagic[] = "070701";
+constexpr size_t kMaxBufferSizeBytes = 1024 * 1024;
+constexpr uint32_t kMaxRingBufferFileAgeSeconds = 60 * 60;
+constexpr char kTombstoneFolderPath[] = "/data/vendor/tombstones/wifi/";
+
template <typename Iface>
void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
iface->invalidate();
@@ -93,6 +104,165 @@
return buffer.data();
}
+// delete files older than a predefined time in the wifi tombstone dir
+bool removeOldFilesInternal() {
+ time_t now = time(0);
+ const time_t delete_files_before = now - kMaxRingBufferFileAgeSeconds;
+ DIR* dir_dump = opendir(kTombstoneFolderPath);
+ if (!dir_dump) {
+ LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+ return false;
+ }
+ unique_fd dir_auto_closer(dirfd(dir_dump));
+ struct dirent* dp;
+ bool success = true;
+ while ((dp = readdir(dir_dump))) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cur_file_name(dp->d_name);
+ struct stat cur_file_stat;
+ std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+ if (stat(cur_file_path.c_str(), &cur_file_stat) != -1) {
+ if (cur_file_stat.st_mtime < delete_files_before) {
+ if (unlink(cur_file_path.c_str()) != 0) {
+ LOG(ERROR) << "Error deleting file " << strerror(errno);
+ success = false;
+ }
+ }
+ } else {
+ LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+ << ": " << strerror(errno);
+ success = false;
+ }
+ }
+ return success;
+}
+
+// Archives all files in |input_dir| and writes result into |out_fd|
+// Logic obtained from //external/toybox/toys/posix/cpio.c "Output cpio archive"
+// portion
+size_t cpioFilesInDir(int out_fd, const char* input_dir) {
+ struct dirent* dp;
+ size_t n_error = 0;
+ char read_buf[32 * 1024];
+ DIR* dir_dump = opendir(input_dir);
+ if (!dir_dump) {
+ LOG(ERROR) << "Failed to open directory: " << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+ unique_fd dir_auto_closer(dirfd(dir_dump));
+ while ((dp = readdir(dir_dump))) {
+ if (dp->d_type != DT_REG) {
+ continue;
+ }
+ std::string cur_file_name(dp->d_name);
+ const size_t file_name_len =
+ cur_file_name.size() + 1; // string.size() does not include the
+ // null terminator. The cpio FreeBSD file
+ // header expects the null character to
+ // be included in the length.
+ struct stat st;
+ ssize_t llen;
+ const std::string cur_file_path = kTombstoneFolderPath + cur_file_name;
+ if (stat(cur_file_path.c_str(), &st) != -1) {
+ const int fd_read = open(cur_file_path.c_str(), O_RDONLY);
+ unique_fd file_auto_closer(fd_read);
+ if (fd_read == -1) {
+ LOG(ERROR) << "Failed to read file " << cur_file_path << " "
+ << strerror(errno);
+ n_error++;
+ continue;
+ }
+ llen = sprintf(
+ read_buf,
+ "%s%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
+ kCpioMagic, static_cast<int>(st.st_ino), st.st_mode, st.st_uid,
+ st.st_gid, static_cast<int>(st.st_nlink),
+ static_cast<int>(st.st_mtime), static_cast<int>(st.st_size),
+ major(st.st_dev), minor(st.st_dev), major(st.st_rdev),
+ minor(st.st_rdev), static_cast<uint32_t>(file_name_len), 0);
+ if (write(out_fd, read_buf, llen) == -1) {
+ LOG(ERROR) << "Error writing cpio header to file "
+ << cur_file_path << " " << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+ if (write(out_fd, cur_file_name.c_str(), file_name_len) == -1) {
+ LOG(ERROR) << "Error writing filename to file " << cur_file_path
+ << " " << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+
+ // NUL Pad header up to 4 multiple bytes.
+ llen = (llen + file_name_len) % 4;
+ if (llen != 0) {
+ const uint32_t zero = 0;
+ if (write(out_fd, &zero, 4 - llen) == -1) {
+ LOG(ERROR) << "Error padding 0s to file " << cur_file_path
+ << " " << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+ }
+
+ // writing content of file
+ llen = st.st_size;
+ while (llen > 0) {
+ ssize_t bytes_read = read(fd_read, read_buf, sizeof(read_buf));
+ if (bytes_read == -1) {
+ LOG(ERROR) << "Error reading file " << cur_file_path << " "
+ << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+ llen -= bytes_read;
+ if (write(out_fd, read_buf, bytes_read) == -1) {
+ LOG(ERROR) << "Error writing data to file " << cur_file_path
+ << " " << strerror(errno);
+ n_error++;
+ return n_error;
+ }
+ if (bytes_read ==
+ 0) { // this should never happen, but just in case
+ // to unstuck from while loop
+ LOG(ERROR) << "Unexpected file size for " << cur_file_path
+ << " " << strerror(errno);
+ n_error++;
+ break;
+ }
+ }
+ llen = st.st_size % 4;
+ if (llen != 0) {
+ const uint32_t zero = 0;
+ write(out_fd, &zero, 4 - llen);
+ }
+ } else {
+ LOG(ERROR) << "Failed to get file stat for " << cur_file_path
+ << ": " << strerror(errno);
+ n_error++;
+ }
+ }
+ memset(read_buf, 0, sizeof(read_buf));
+ if (write(out_fd, read_buf,
+ sprintf(read_buf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0) +
+ 4) == -1) {
+ LOG(ERROR) << "Error writing trailing bytes " << strerror(errno);
+ n_error++;
+ }
+ return n_error;
+}
+
+// Helper function to create a non-const char*.
+std::vector<char> makeCharVec(const std::string& str) {
+ std::vector<char> vec(str.size() + 1);
+ vec.assign(str.begin(), str.end());
+ vec.push_back('\0');
+ return vec;
+}
+
} // namespace
namespace android {
@@ -136,7 +306,7 @@
}
Return<void> WifiChip::registerEventCallback(
- const sp<IWifiChipEventCallback>& event_callback,
+ const sp<V1_0::IWifiChipEventCallback>& event_callback,
registerEventCallback_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
&WifiChip::registerEventCallbackInternal,
@@ -337,7 +507,7 @@
}
Return<void> WifiChip::selectTxPowerScenario(
- TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
+ V1_1::IWifiChip::TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
&WifiChip::selectTxPowerScenarioInternal,
hidl_status_cb, scenario);
@@ -350,6 +520,38 @@
hidl_status_cb);
}
+Return<void> WifiChip::registerEventCallback_1_2(
+ const sp<IWifiChipEventCallback>& event_callback,
+ registerEventCallback_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::registerEventCallbackInternal_1_2,
+ hidl_status_cb, event_callback);
+}
+
+Return<void> WifiChip::selectTxPowerScenario_1_2(
+ TxPowerScenario scenario, selectTxPowerScenario_cb hidl_status_cb) {
+ return validateAndCall(this, WifiStatusCode::ERROR_WIFI_CHIP_INVALID,
+ &WifiChip::selectTxPowerScenarioInternal_1_2, hidl_status_cb, scenario);
+}
+
+Return<void> WifiChip::debug(const hidl_handle& handle,
+ const hidl_vec<hidl_string>&) {
+ if (handle != nullptr && handle->numFds >= 1) {
+ int fd = handle->data[0];
+ if (!writeRingbufferFilesInternal()) {
+ LOG(ERROR) << "Error writing files to flash";
+ }
+ uint32_t n_error = cpioFilesInDir(fd, kTombstoneFolderPath);
+ if (n_error != 0) {
+ LOG(ERROR) << n_error << " errors occured in cpio function";
+ }
+ fsync(fd);
+ } else {
+ LOG(ERROR) << "File handle error";
+ }
+ return Void();
+}
+
void WifiChip::invalidateAndRemoveAllIfaces() {
invalidateAndClearAll(ap_ifaces_);
invalidateAndClearAll(nan_ifaces_);
@@ -368,11 +570,9 @@
}
WifiStatus WifiChip::registerEventCallbackInternal(
- const sp<IWifiChipEventCallback>& event_callback) {
- if (!event_cb_handler_.addCallback(event_callback)) {
- return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
- }
- return createWifiStatus(WifiStatusCode::SUCCESS);
+ const sp<V1_0::IWifiChipEventCallback>& /* event_callback */) {
+ // Deprecated support for this callback.
+ return createWifiStatus(WifiStatusCode::ERROR_NOT_SUPPORTED);
}
std::pair<WifiStatus, uint32_t> WifiChip::getCapabilitiesInternal() {
@@ -727,6 +927,8 @@
std::underlying_type<WifiDebugRingBufferVerboseLevel>::type>(
verbose_level),
max_interval_in_sec, min_data_size_in_bytes);
+ ringbuffer_map_.insert(std::pair<std::string, Ringbuffer>(
+ ring_name, Ringbuffer(kMaxBufferSizeBytes)));
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -738,6 +940,7 @@
}
legacy_hal::wifi_error legacy_status =
legacy_hal_.lock()->getRingBufferData(getWlan0IfaceName(), ring_name);
+
return createWifiStatusFromLegacyError(legacy_status);
}
@@ -793,7 +996,8 @@
return createWifiStatusFromLegacyError(legacy_status);
}
-WifiStatus WifiChip::selectTxPowerScenarioInternal(TxPowerScenario scenario) {
+WifiStatus WifiChip::selectTxPowerScenarioInternal(
+ V1_1::IWifiChip::TxPowerScenario scenario) {
auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
getWlan0IfaceName(),
hidl_struct_util::convertHidlTxPowerScenarioToLegacy(scenario));
@@ -806,6 +1010,21 @@
return createWifiStatusFromLegacyError(legacy_status);
}
+WifiStatus WifiChip::registerEventCallbackInternal_1_2(
+ const sp<IWifiChipEventCallback>& event_callback) {
+ if (!event_cb_handler_.addCallback(event_callback)) {
+ return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+ }
+ return createWifiStatus(WifiStatusCode::SUCCESS);
+}
+
+WifiStatus WifiChip::selectTxPowerScenarioInternal_1_2(TxPowerScenario scenario) {
+ auto legacy_status = legacy_hal_.lock()->selectTxPowerScenario(
+ getWlan0IfaceName(),
+ hidl_struct_util::convertHidlTxPowerScenarioToLegacy_1_2(scenario));
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
WifiStatus WifiChip::handleChipConfiguration(
/* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
ChipModeId mode_id) {
@@ -839,6 +1058,13 @@
<< legacyErrorToString(legacy_status);
return createWifiStatusFromLegacyError(legacy_status);
}
+ // Every time the HAL is restarted, we need to register the
+ // radio mode change callback.
+ WifiStatus status = registerRadioModeChangeCallback();
+ if (status.code != WifiStatusCode::SUCCESS) {
+ // This probably is not a critical failure?
+ LOG(ERROR) << "Failed to register radio mode change callback";
+ }
return createWifiStatus(WifiStatusCode::SUCCESS);
}
@@ -849,7 +1075,7 @@
android::wp<WifiChip> weak_ptr_this(this);
const auto& on_ring_buffer_data_callback =
- [weak_ptr_this](const std::string& /* name */,
+ [weak_ptr_this](const std::string& name,
const std::vector<uint8_t>& data,
const legacy_hal::wifi_ring_buffer_status& status) {
const auto shared_ptr_this = weak_ptr_this.promote();
@@ -863,13 +1089,13 @@
LOG(ERROR) << "Error converting ring buffer status";
return;
}
- for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
- if (!callback->onDebugRingBufferDataAvailable(hidl_status, data)
- .isOk()) {
- LOG(ERROR)
- << "Failed to invoke onDebugRingBufferDataAvailable"
- << " callback on: " << toString(callback);
- }
+ const auto& target = shared_ptr_this->ringbuffer_map_.find(name);
+ if (target != shared_ptr_this->ringbuffer_map_.end()) {
+ Ringbuffer& cur_buffer = target->second;
+ cur_buffer.append(data);
+ } else {
+ LOG(ERROR) << "Ringname " << name << " not found";
+ return;
}
};
legacy_hal::wifi_error legacy_status =
@@ -882,6 +1108,36 @@
return createWifiStatusFromLegacyError(legacy_status);
}
+WifiStatus WifiChip::registerRadioModeChangeCallback() {
+ android::wp<WifiChip> weak_ptr_this(this);
+ const auto& on_radio_mode_change_callback =
+ [weak_ptr_this](const std::vector<legacy_hal::WifiMacInfo>& mac_infos) {
+ const auto shared_ptr_this = weak_ptr_this.promote();
+ if (!shared_ptr_this.get() || !shared_ptr_this->isValid()) {
+ LOG(ERROR) << "Callback invoked on an invalid object";
+ return;
+ }
+ std::vector<IWifiChipEventCallback::RadioModeInfo>
+ hidl_radio_mode_infos;
+ if (!hidl_struct_util::convertLegacyWifiMacInfosToHidl(
+ mac_infos, &hidl_radio_mode_infos)) {
+ LOG(ERROR) << "Error converting wifi mac info";
+ return;
+ }
+ for (const auto& callback : shared_ptr_this->getEventCallbacks()) {
+ if (!callback->onRadioModeChange(hidl_radio_mode_infos)
+ .isOk()) {
+ LOG(ERROR) << "Failed to invoke onRadioModeChange"
+ << " callback on: " << toString(callback);
+ }
+ }
+ };
+ legacy_hal::wifi_error legacy_status =
+ legacy_hal_.lock()->registerRadioModeChangeCallbackHandler(
+ getWlan0IfaceName(), on_radio_mode_change_callback);
+ return createWifiStatusFromLegacyError(legacy_status);
+}
+
void WifiChip::populateModes() {
// The chip combination supported for current devices is fixed.
// They can be one of the following based on device features:
@@ -1080,6 +1336,35 @@
return {};
}
+bool WifiChip::writeRingbufferFilesInternal() {
+ if (!removeOldFilesInternal()) {
+ LOG(ERROR) << "Error occurred while deleting old tombstone files";
+ return false;
+ }
+ // write ringbuffers to file
+ for (const auto& item : ringbuffer_map_) {
+ const Ringbuffer& cur_buffer = item.second;
+ if (cur_buffer.getData().empty()) {
+ continue;
+ }
+ const std::string file_path_raw =
+ kTombstoneFolderPath + item.first + "XXXXXXXXXX";
+ const int dump_fd = mkstemp(makeCharVec(file_path_raw).data());
+ if (dump_fd == -1) {
+ LOG(ERROR) << "create file failed: " << strerror(errno);
+ return false;
+ }
+ unique_fd file_auto_closer(dump_fd);
+ for (const auto& cur_block : cur_buffer.getData()) {
+ if (write(dump_fd, cur_block.data(),
+ sizeof(cur_block[0]) * cur_block.size()) == -1) {
+ LOG(ERROR) << "Error writing to file " << strerror(errno);
+ }
+ }
+ }
+ return true;
+}
+
} // namespace implementation
} // namespace V1_2
} // namespace wifi
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index b5dcc8c..ada9458 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -17,12 +17,14 @@
#ifndef WIFI_CHIP_H_
#define WIFI_CHIP_H_
+#include <list>
#include <map>
#include <android-base/macros.h>
#include <android/hardware/wifi/1.2/IWifiChip.h>
#include "hidl_callback_util.h"
+#include "ringbuffer.h"
#include "wifi_ap_iface.h"
#include "wifi_feature_flags.h"
#include "wifi_legacy_hal.h"
@@ -72,7 +74,7 @@
// HIDL methods exposed.
Return<void> getId(getId_cb hidl_status_cb) override;
Return<void> registerEventCallback(
- const sp<IWifiChipEventCallback>& event_callback,
+ const sp<V1_0::IWifiChipEventCallback>& event_callback,
registerEventCallback_cb hidl_status_cb) override;
Return<void> getCapabilities(getCapabilities_cb hidl_status_cb) override;
Return<void> getAvailableModes(
@@ -130,18 +132,25 @@
Return<void> enableDebugErrorAlerts(
bool enable, enableDebugErrorAlerts_cb hidl_status_cb) override;
Return<void> selectTxPowerScenario(
- TxPowerScenario scenario,
+ V1_1::IWifiChip::TxPowerScenario scenario,
selectTxPowerScenario_cb hidl_status_cb) override;
Return<void> resetTxPowerScenario(
resetTxPowerScenario_cb hidl_status_cb) override;
-
+ Return<void> registerEventCallback_1_2(
+ const sp<IWifiChipEventCallback>& event_callback,
+ registerEventCallback_1_2_cb hidl_status_cb) override;
+ Return<void> selectTxPowerScenario_1_2(
+ TxPowerScenario scenario,
+ selectTxPowerScenario_cb hidl_status_cb) override;
+ Return<void> debug(const hidl_handle& handle,
+ const hidl_vec<hidl_string>& options) override;
private:
void invalidateAndRemoveAllIfaces();
// Corresponding worker functions for the HIDL methods.
std::pair<WifiStatus, ChipId> getIdInternal();
WifiStatus registerEventCallbackInternal(
- const sp<IWifiChipEventCallback>& event_callback);
+ const sp<V1_0::IWifiChipEventCallback>& event_callback);
std::pair<WifiStatus, uint32_t> getCapabilitiesInternal();
std::pair<WifiStatus, std::vector<ChipMode>> getAvailableModesInternal();
WifiStatus configureChipInternal(
@@ -186,12 +195,15 @@
std::pair<WifiStatus, WifiDebugHostWakeReasonStats>
getDebugHostWakeReasonStatsInternal();
WifiStatus enableDebugErrorAlertsInternal(bool enable);
- WifiStatus selectTxPowerScenarioInternal(TxPowerScenario scenario);
+ WifiStatus selectTxPowerScenarioInternal(V1_1::IWifiChip::TxPowerScenario scenario);
WifiStatus resetTxPowerScenarioInternal();
-
+ WifiStatus registerEventCallbackInternal_1_2(
+ const sp<IWifiChipEventCallback>& event_callback);
+ WifiStatus selectTxPowerScenarioInternal_1_2(TxPowerScenario scenario);
WifiStatus handleChipConfiguration(
std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
WifiStatus registerDebugRingBufferCallback();
+ WifiStatus registerRadioModeChangeCallback();
void populateModes();
std::vector<IWifiChip::ChipIfaceCombination>
@@ -204,6 +216,7 @@
bool canCurrentModeSupportIfaceOfType(IfaceType type);
bool isValidModeId(ChipModeId mode_id);
std::string allocateApOrStaIfaceName();
+ bool writeRingbufferFilesInternal();
ChipId chip_id_;
std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
@@ -214,6 +227,7 @@
std::vector<sp<WifiP2pIface>> p2p_ifaces_;
std::vector<sp<WifiStaIface>> sta_ifaces_;
std::vector<sp<WifiRttController>> rtt_controllers_;
+ std::map<std::string, Ringbuffer> ringbuffer_map_;
bool is_valid_;
// Members pertaining to chip configuration.
uint32_t current_mode_id_;
diff --git a/wifi/1.2/default/wifi_legacy_hal.cpp b/wifi/1.2/default/wifi_legacy_hal.cpp
index 9abe514..5f40d50 100644
--- a/wifi/1.2/default/wifi_legacy_hal.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal.cpp
@@ -148,6 +148,17 @@
}
}
+// Callback to be invoked for radio mode change indication.
+std::function<void(wifi_request_id, uint32_t, wifi_mac_info*)>
+ on_radio_mode_change_internal_callback;
+void onAsyncRadioModeChange(wifi_request_id id, uint32_t num_macs,
+ wifi_mac_info* mac_infos) {
+ const auto lock = hidl_sync_util::acquireGlobalLock();
+ if (on_radio_mode_change_internal_callback) {
+ on_radio_mode_change_internal_callback(id, num_macs, mac_infos);
+ }
+}
+
// Callback to be invoked for rtt results results.
std::function<void(wifi_request_id, unsigned num_results,
wifi_rtt_result* rtt_results[])>
@@ -937,6 +948,41 @@
0, getIfaceHandle(iface_name));
}
+wifi_error WifiLegacyHal::registerRadioModeChangeCallbackHandler(
+ const std::string& iface_name,
+ const on_radio_mode_change_callback& on_user_change_callback) {
+ if (on_radio_mode_change_internal_callback) {
+ return WIFI_ERROR_NOT_AVAILABLE;
+ }
+ on_radio_mode_change_internal_callback = [on_user_change_callback](
+ wifi_request_id /* id */,
+ uint32_t num_macs,
+ wifi_mac_info* mac_infos_arr) {
+ if (num_macs > 0 && mac_infos_arr) {
+ std::vector<WifiMacInfo> mac_infos_vec;
+ for (uint32_t i = 0; i < num_macs; i++) {
+ WifiMacInfo mac_info;
+ mac_info.wlan_mac_id = mac_infos_arr[i].wlan_mac_id;
+ mac_info.mac_band = mac_infos_arr[i].mac_band;
+ for (int32_t j = 0; j < mac_infos_arr[i].num_iface; j++) {
+ WifiIfaceInfo iface_info;
+ iface_info.name = mac_infos_arr[i].iface_info[j].iface_name;
+ iface_info.channel = mac_infos_arr[i].iface_info[j].channel;
+ mac_info.iface_infos.push_back(iface_info);
+ }
+ mac_infos_vec.push_back(mac_info);
+ }
+ on_user_change_callback(mac_infos_vec);
+ }
+ };
+ wifi_error status = global_func_table_.wifi_set_radio_mode_change_handler(
+ 0, getIfaceHandle(iface_name), {onAsyncRadioModeChange});
+ if (status != WIFI_SUCCESS) {
+ on_radio_mode_change_internal_callback = nullptr;
+ }
+ return status;
+}
+
wifi_error WifiLegacyHal::startRttRangeRequest(
const std::string& iface_name, wifi_request_id id,
const std::vector<wifi_rtt_config>& rtt_configs,
@@ -1324,6 +1370,7 @@
on_rssi_threshold_breached_internal_callback = nullptr;
on_ring_buffer_data_internal_callback = nullptr;
on_error_alert_internal_callback = nullptr;
+ on_radio_mode_change_internal_callback = nullptr;
on_rtt_results_internal_callback = nullptr;
on_nan_notify_response_user_callback = nullptr;
on_nan_event_publish_terminated_user_callback = nullptr;
diff --git a/wifi/1.2/default/wifi_legacy_hal.h b/wifi/1.2/default/wifi_legacy_hal.h
index da88f6b..bd68bcd 100644
--- a/wifi/1.2/default/wifi_legacy_hal.h
+++ b/wifi/1.2/default/wifi_legacy_hal.h
@@ -132,6 +132,26 @@
// Callback for alerts.
using on_error_alert_callback =
std::function<void(int32_t, const std::vector<uint8_t>&)>;
+
+// Struct for the mac info from the legacy HAL. This is a cleaner version
+// of the |wifi_mac_info| & |wifi_iface_info|.
+typedef struct {
+ std::string name;
+ wifi_channel channel;
+} WifiIfaceInfo;
+
+typedef struct {
+ uint32_t wlan_mac_id;
+ /* BIT MASK of BIT(WLAN_MAC*) as represented by wlan_mac_band */
+ uint32_t mac_band;
+ /* Represents the connected Wi-Fi interfaces associated with each MAC */
+ std::vector<WifiIfaceInfo> iface_infos;
+} WifiMacInfo;
+
+// Callback for radio mode change
+using on_radio_mode_change_callback =
+ std::function<void(const std::vector<WifiMacInfo>&)>;
+
/**
* Class that encapsulates all legacy HAL interactions.
* This class manages the lifetime of the event loop thread used by legacy HAL.
@@ -253,6 +273,10 @@
const on_error_alert_callback& on_alert_callback);
wifi_error deregisterErrorAlertCallbackHandler(
const std::string& iface_name);
+ // Radio mode functions.
+ wifi_error registerRadioModeChangeCallbackHandler(
+ const std::string& iface_name,
+ const on_radio_mode_change_callback& on_user_change_callback);
// RTT functions.
wifi_error startRttRangeRequest(
const std::string& iface_name, wifi_request_id id,
diff --git a/wifi/1.2/default/wifi_legacy_hal_stubs.cpp b/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
index 28972cb..2ee2aa2 100644
--- a/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal_stubs.cpp
@@ -134,6 +134,7 @@
populateStubFor(&hal_fn->wifi_configure_roaming);
populateStubFor(&hal_fn->wifi_select_tx_power_scenario);
populateStubFor(&hal_fn->wifi_reset_tx_power_scenario);
+ populateStubFor(&hal_fn->wifi_set_radio_mode_change_handler);
return true;
}
} // namespace legacy_hal
diff --git a/wifi/hostapd/1.0/vts/functional/Android.bp b/wifi/hostapd/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..7a920b4
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/Android.bp
@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_static {
+ name: "VtsHalWifiHostapdV1_0TargetTestUtil",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: ["hostapd_hidl_test_utils.cpp"],
+ export_include_dirs: [
+ "."
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "android.hardware.wifi.hostapd@1.0",
+ "android.hardware.wifi@1.0",
+ "libcrypto",
+ "libgmock",
+ "libwifi-system",
+ "libwifi-system-iface",
+ ],
+}
+
+cc_test {
+ name: "VtsHalWifiHostapdV1_0TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "VtsHalWifiHostapdV1_0TargetTest.cpp",
+ "hostapd_hidl_test.cpp",
+ ],
+ static_libs: [
+ "VtsHalWifiV1_0TargetTestUtil",
+ "VtsHalWifiHostapdV1_0TargetTestUtil",
+ "android.hardware.wifi.hostapd@1.0",
+ "android.hardware.wifi@1.0",
+ "libcrypto",
+ "libgmock",
+ "libwifi-system",
+ "libwifi-system-iface",
+ ],
+}
diff --git a/wifi/hostapd/1.0/vts/functional/VtsHalWifiHostapdV1_0TargetTest.cpp b/wifi/hostapd/1.0/vts/functional/VtsHalWifiHostapdV1_0TargetTest.cpp
new file mode 100644
index 0000000..64e6fbe
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/VtsHalWifiHostapdV1_0TargetTest.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include "hostapd_hidl_test_utils.h"
+
+class HostapdHidlEnvironment : public ::testing::Environment {
+ public:
+ virtual void SetUp() override { stopHostapd(); }
+ virtual void TearDown() override { startHostapdAndWaitForHidlService(); }
+};
+
+int main(int argc, char** argv) {
+ ::testing::AddGlobalTestEnvironment(new HostapdHidlEnvironment);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
new file mode 100644
index 0000000..2f71ccb
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file is copied from
+// hardware/interfaces/wifi/1.0/vts/functional/wifi_hidl_call_util.h
+// Please make sure these two file are consistent.
+
+#pragma once
+
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+namespace {
+namespace detail {
+template <typename>
+struct functionArgSaver;
+
+// Provides a std::function that takes one argument, and a buffer
+// wherein the function will store its argument. The buffer has
+// the same type as the argument, but with const and reference
+// modifiers removed.
+template <typename ArgT>
+struct functionArgSaver<std::function<void(ArgT)>> final {
+ using StorageT = typename std::remove_const<
+ typename std::remove_reference<ArgT>::type>::type;
+
+ std::function<void(ArgT)> saveArgs = [this](ArgT arg) {
+ this->saved_values = arg;
+ };
+
+ StorageT saved_values;
+};
+
+// Provides a std::function that takes two arguments, and a buffer
+// wherein the function will store its arguments. The buffer is a
+// std::pair, whose elements have the same types as the arguments
+// (but with const and reference modifiers removed).
+template <typename Arg1T, typename Arg2T>
+struct functionArgSaver<std::function<void(Arg1T, Arg2T)>> final {
+ using StorageT =
+ std::pair<typename std::remove_const<
+ typename std::remove_reference<Arg1T>::type>::type,
+ typename std::remove_const<
+ typename std::remove_reference<Arg2T>::type>::type>;
+
+ std::function<void(Arg1T, Arg2T)> saveArgs = [this](Arg1T arg1,
+ Arg2T arg2) {
+ this->saved_values = {arg1, arg2};
+ };
+
+ StorageT saved_values;
+};
+
+// Provides a std::function that takes three or more arguments, and a
+// buffer wherein the function will store its arguments. The buffer is a
+// std::tuple whose elements have the same types as the arguments (but
+// with const and reference modifiers removed).
+template <typename... ArgT>
+struct functionArgSaver<std::function<void(ArgT...)>> final {
+ using StorageT = std::tuple<typename std::remove_const<
+ typename std::remove_reference<ArgT>::type>::type...>;
+
+ std::function<void(ArgT...)> saveArgs = [this](ArgT... arg) {
+ this->saved_values = {arg...};
+ };
+
+ StorageT saved_values;
+};
+
+// Invokes |method| on |object|, providing |method| a CallbackT as the
+// final argument. Returns a copy of the parameters that |method| provided
+// to CallbackT. (The parameters are returned by value.)
+template <typename CallbackT, typename MethodT, typename ObjectT,
+ typename... ArgT>
+typename functionArgSaver<CallbackT>::StorageT invokeMethod(
+ MethodT method, ObjectT object, ArgT&&... methodArg) {
+ functionArgSaver<CallbackT> result_buffer;
+ const auto& res = ((*object).*method)(std::forward<ArgT>(methodArg)...,
+ result_buffer.saveArgs);
+ EXPECT_TRUE(res.isOk());
+ return result_buffer.saved_values;
+}
+} // namespace detail
+} // namespace
+
+// Invokes |method| on |strong_pointer|, passing provided arguments through to
+// |method|.
+//
+// Returns either:
+// - A copy of the result callback parameter (for callbacks with a single
+// parameter), OR
+// - A pair containing a copy of the result callback parameters (for callbacks
+// with two parameters), OR
+// - A tuple containing a copy of the result callback paramters (for callbacks
+// with three or more parameters).
+//
+// Example usage:
+// EXPECT_EQ(HostapdStatusCode::SUCCESS,
+// HIDL_INVOKE(strong_pointer, methodReturningHostapdStatus).code);
+// EXPECT_EQ(HostapdStatusCode::SUCCESS,
+// HIDL_INVOKE(strong_pointer, methodReturningHostapdStatusAndOneMore)
+// .first.code);
+// EXPECT_EQ(HostapdStatusCode::SUCCESS, std::get<0>(
+// HIDL_INVOKE(strong_pointer, methodReturningHostapdStatusAndTwoMore))
+// .code);
+#define HIDL_INVOKE(strong_pointer, method, ...) \
+ (detail::invokeMethod< \
+ std::remove_reference<decltype(*strong_pointer)>::type::method##_cb>( \
+ &std::remove_reference<decltype(*strong_pointer)>::type::method, \
+ strong_pointer, ##__VA_ARGS__))
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
new file mode 100644
index 0000000..5f51cfb
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+#include <android/hardware/wifi/hostapd/1.0/IHostapd.h>
+
+#include "hostapd_hidl_call_util.h"
+#include "hostapd_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::wifi::hostapd::V1_0::IHostapd;
+using ::android::hardware::wifi::hostapd::V1_0::HostapdStatus;
+using ::android::hardware::wifi::hostapd::V1_0::HostapdStatusCode;
+
+namespace {
+constexpr unsigned char kNwSsid[] = {'t', 'e', 's', 't', '1',
+ '2', '3', '4', '5'};
+constexpr char kNwPassphrase[] = "test12345";
+constexpr int kIfaceChannel = 6;
+constexpr int kIfaceInvalidChannel = 567;
+} // namespace
+
+class HostapdHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ startHostapdAndWaitForHidlService();
+ hostapd_ = getHostapd();
+ ASSERT_NE(hostapd_.get(), nullptr);
+ }
+
+ virtual void TearDown() override { stopHostapd(); }
+
+ protected:
+ std::string getPrimaryWlanIfaceName() {
+ std::array<char, PROPERTY_VALUE_MAX> buffer;
+ property_get("wifi.interface", buffer.data(), "wlan0");
+ return buffer.data();
+ }
+
+ IHostapd::IfaceParams getIfaceParamsWithAcs() {
+ IHostapd::IfaceParams iface_params;
+ iface_params.ifaceName = getPrimaryWlanIfaceName();
+ iface_params.hwModeParams.enable80211N = true;
+ iface_params.hwModeParams.enable80211AC = false;
+ iface_params.channelParams.enableAcs = true;
+ iface_params.channelParams.acsShouldExcludeDfs = true;
+ iface_params.channelParams.channel = 0;
+ iface_params.channelParams.band = IHostapd::Band::BAND_ANY;
+ return iface_params;
+ }
+
+ IHostapd::IfaceParams getIfaceParamsWithoutAcs() {
+ IHostapd::IfaceParams iface_params;
+ iface_params.ifaceName = getPrimaryWlanIfaceName();
+ iface_params.hwModeParams.enable80211N = true;
+ iface_params.hwModeParams.enable80211AC = false;
+ iface_params.channelParams.enableAcs = false;
+ iface_params.channelParams.acsShouldExcludeDfs = false;
+ iface_params.channelParams.channel = kIfaceChannel;
+ iface_params.channelParams.band = IHostapd::Band::BAND_2_4_GHZ;
+ return iface_params;
+ }
+
+ IHostapd::IfaceParams getIfaceParamsWithInvalidChannel() {
+ IHostapd::IfaceParams iface_params;
+ iface_params.ifaceName = getPrimaryWlanIfaceName();
+ iface_params.hwModeParams.enable80211N = true;
+ iface_params.hwModeParams.enable80211AC = false;
+ iface_params.channelParams.enableAcs = false;
+ iface_params.channelParams.acsShouldExcludeDfs = false;
+ iface_params.channelParams.channel = kIfaceInvalidChannel;
+ iface_params.channelParams.band = IHostapd::Band::BAND_2_4_GHZ;
+ return iface_params;
+ }
+
+ IHostapd::NetworkParams getPskNwParams() {
+ IHostapd::NetworkParams nw_params;
+ nw_params.ssid =
+ std::vector<uint8_t>(kNwSsid, kNwSsid + sizeof(kNwSsid));
+ nw_params.isHidden = false;
+ nw_params.encryptionType = IHostapd::EncryptionType::WPA2;
+ nw_params.pskPassphrase = kNwPassphrase;
+ return nw_params;
+ }
+
+ IHostapd::NetworkParams getInvalidPskNwParams() {
+ IHostapd::NetworkParams nw_params;
+ nw_params.ssid =
+ std::vector<uint8_t>(kNwSsid, kNwSsid + sizeof(kNwSsid));
+ nw_params.isHidden = false;
+ nw_params.encryptionType = IHostapd::EncryptionType::WPA2;
+ return nw_params;
+ }
+
+ IHostapd::NetworkParams getOpenNwParams() {
+ IHostapd::NetworkParams nw_params;
+ nw_params.ssid =
+ std::vector<uint8_t>(kNwSsid, kNwSsid + sizeof(kNwSsid));
+ nw_params.isHidden = false;
+ nw_params.encryptionType = IHostapd::EncryptionType::NONE;
+ return nw_params;
+ }
+ // IHostapd object used for all tests in this fixture.
+ sp<IHostapd> hostapd_;
+};
+
+/*
+ * Create:
+ * Ensures that an instance of the IHostapd proxy object is
+ * successfully created.
+ */
+TEST(HostapdHidlTestNoFixture, Create) {
+ startHostapdAndWaitForHidlService();
+ EXPECT_NE(nullptr, getHostapd().get());
+ stopHostapd();
+}
+
+/**
+ * Adds an access point with PSK network config & ACS enabled.
+ * Access point creation should pass.
+ */
+TEST_F(HostapdHidlTest, AddPskAccessPointWithAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint, getIfaceParamsWithAcs(),
+ getPskNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds an access point with Open network config & ACS enabled.
+ * Access point creation should pass.
+ */
+TEST_F(HostapdHidlTest, AddOpenAccessPointWithAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint, getIfaceParamsWithAcs(),
+ getOpenNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds an access point with PSK network config & ACS disabled.
+ * Access point creation should pass.
+ */
+TEST_F(HostapdHidlTest, AddPskAccessPointWithoutAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint,
+ getIfaceParamsWithoutAcs(), getPskNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds an access point with Open network config & ACS disabled.
+ * Access point creation should pass.
+ */
+TEST_F(HostapdHidlTest, AddOpenAccessPointWithoutAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint,
+ getIfaceParamsWithoutAcs(), getOpenNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds & then removes an access point with PSK network config & ACS enabled.
+ * Access point creation & removal should pass.
+ */
+TEST_F(HostapdHidlTest, RemoveAccessPointWithAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint, getIfaceParamsWithAcs(),
+ getPskNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+ status =
+ HIDL_INVOKE(hostapd_, removeAccessPoint, getPrimaryWlanIfaceName());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds & then removes an access point with PSK network config & ACS disabled.
+ * Access point creation & removal should pass.
+ */
+TEST_F(HostapdHidlTest, RemoveAccessPointWithoutAcs) {
+ auto status = HIDL_INVOKE(hostapd_, addAccessPoint,
+ getIfaceParamsWithoutAcs(), getPskNwParams());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+ status =
+ HIDL_INVOKE(hostapd_, removeAccessPoint, getPrimaryWlanIfaceName());
+ EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds an access point with invalid channel.
+ * Access point creation should fail.
+ */
+TEST_F(HostapdHidlTest, AddPskAccessPointWithInvalidChannel) {
+ auto status =
+ HIDL_INVOKE(hostapd_, addAccessPoint,
+ getIfaceParamsWithInvalidChannel(), getPskNwParams());
+ EXPECT_NE(HostapdStatusCode::SUCCESS, status.code);
+}
+
+/**
+ * Adds an access point with invalid PSK network config.
+ * Access point creation should fail.
+ */
+TEST_F(HostapdHidlTest, AddInvalidPskAccessPointWithoutAcs) {
+ auto status =
+ HIDL_INVOKE(hostapd_, addAccessPoint, getIfaceParamsWithoutAcs(),
+ getInvalidPskNwParams());
+ EXPECT_NE(HostapdStatusCode::SUCCESS, status.code);
+}
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
new file mode 100644
index 0000000..0915150
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hidl/manager/1.0/IServiceNotification.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <wifi_system/hostapd_manager.h>
+#include <wifi_system/interface_tool.h>
+
+#include "hostapd_hidl_test_utils.h"
+#include "wifi_hidl_test_utils.h"
+
+using ::android::sp;
+using ::android::hardware::configureRpcThreadpool;
+using ::android::hardware::joinRpcThreadpool;
+using ::android::hardware::hidl_string;
+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::IWifiChip;
+using ::android::hardware::wifi::hostapd::V1_0::IHostapd;
+using ::android::hardware::wifi::hostapd::V1_0::HostapdStatus;
+using ::android::hardware::wifi::hostapd::V1_0::HostapdStatusCode;
+using ::android::hidl::manager::V1_0::IServiceNotification;
+using ::android::wifi_system::HostapdManager;
+
+namespace {
+const char kHostapdServiceName[] = "default";
+
+// Helper function to initialize the driver and firmware to AP mode
+// using the vendor HAL HIDL interface.
+void initilializeDriverAndFirmware() {
+ sp<IWifiChip> wifi_chip = getWifiChip();
+ ChipModeId mode_id;
+ EXPECT_TRUE(configureChipToSupportIfaceType(
+ wifi_chip, ::android::hardware::wifi::V1_0::IfaceType::AP, &mode_id));
+}
+
+// Helper function to deinitialize the driver and firmware
+// using the vendor HAL HIDL interface.
+void deInitilializeDriverAndFirmware() { stopWifi(); }
+} // namespace
+
+// Utility class to wait for wpa_hostapd's HIDL service registration.
+class ServiceNotificationListener : public IServiceNotification {
+ public:
+ Return<void> onRegistration(const hidl_string& fully_qualified_name,
+ const hidl_string& instance_name,
+ bool pre_existing) override {
+ if (pre_existing) {
+ return Void();
+ }
+ std::unique_lock<std::mutex> lock(mutex_);
+ registered_.push_back(std::string(fully_qualified_name.c_str()) + "/" +
+ instance_name.c_str());
+ lock.unlock();
+ condition_.notify_one();
+ return Void();
+ }
+
+ bool registerForHidlServiceNotifications(const std::string& instance_name) {
+ if (!IHostapd::registerForNotifications(instance_name, this)) {
+ return false;
+ }
+ configureRpcThreadpool(2, false);
+ return true;
+ }
+
+ bool waitForHidlService(uint32_t timeout_in_millis,
+ const std::string& instance_name) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ condition_.wait_for(lock, std::chrono::milliseconds(timeout_in_millis),
+ [&]() { return registered_.size() >= 1; });
+ if (registered_.size() != 1) {
+ return false;
+ }
+ std::string expected_registered =
+ std::string(IHostapd::descriptor) + "/" + instance_name;
+ if (registered_[0] != expected_registered) {
+ LOG(ERROR) << "Expected: " << expected_registered
+ << ", Got: " << registered_[0];
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ std::vector<std::string> registered_{};
+ std::mutex mutex_;
+ std::condition_variable condition_;
+};
+
+void stopHostapd() {
+ HostapdManager hostapd_manager;
+
+ ASSERT_TRUE(hostapd_manager.StopHostapd());
+ deInitilializeDriverAndFirmware();
+}
+
+void startHostapdAndWaitForHidlService() {
+ initilializeDriverAndFirmware();
+
+ android::sp<ServiceNotificationListener> notification_listener =
+ new ServiceNotificationListener();
+ ASSERT_TRUE(notification_listener->registerForHidlServiceNotifications(
+ kHostapdServiceName));
+
+ HostapdManager hostapd_manager;
+ ASSERT_TRUE(hostapd_manager.StartHostapd());
+
+ ASSERT_TRUE(
+ notification_listener->waitForHidlService(200, kHostapdServiceName));
+}
+
+sp<IHostapd> getHostapd() {
+ return ::testing::VtsHalHidlTargetTestBase::getService<IHostapd>();
+}
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h
new file mode 100644
index 0000000..74ed284
--- /dev/null
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test_utils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HOSTAPD_HIDL_TEST_UTILS_H
+#define HOSTAPD_HIDL_TEST_UTILS_H
+
+#include <android/hardware/wifi/hostapd/1.0/IHostapd.h>
+
+// Used to stop the android wifi framework before every test.
+void stopWifiFramework();
+void startWifiFramework();
+void stopHostapd();
+// Used to configure the chip, driver and start wpa_hostapd before every
+// test.
+void startHostapdAndWaitForHidlService();
+
+// Helper functions to obtain references to the various HIDL interface objects.
+// Note: We only have a single instance of each of these objects currently.
+// These helper functions should be modified to return vectors if we support
+// multiple instances.
+android::sp<android::hardware::wifi::hostapd::V1_0::IHostapd> getHostapd();
+
+#endif /* HOSTAPD_HIDL_TEST_UTILS_H */
diff --git a/wifi/supplicant/1.1/Android.bp b/wifi/supplicant/1.1/Android.bp
index c8c8a32..fafd6ad 100644
--- a/wifi/supplicant/1.1/Android.bp
+++ b/wifi/supplicant/1.1/Android.bp
@@ -8,6 +8,7 @@
},
srcs: [
"ISupplicant.hal",
+ "ISupplicantStaNetwork.hal",
],
interfaces: [
"android.hardware.wifi.supplicant@1.0",
diff --git a/wifi/supplicant/1.1/ISupplicantStaNetwork.hal b/wifi/supplicant/1.1/ISupplicantStaNetwork.hal
new file mode 100644
index 0000000..186fe75
--- /dev/null
+++ b/wifi/supplicant/1.1/ISupplicantStaNetwork.hal
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.wifi.supplicant@1.1;
+
+import @1.0::ISupplicantNetwork;
+import @1.0::ISupplicantStaNetworkCallback;
+import @1.0::SupplicantStatus;
+import @1.0::ISupplicantStaNetwork;
+
+/**
+ * Interface exposed by the supplicant for each station mode network
+ * configuration it controls.
+ */
+interface ISupplicantStaNetwork extends @1.0::ISupplicantStaNetwork {
+ /**
+ * EAP IMSI Identity to be used for authentication to EAP SIM networks.
+ * The identity must be derived from the IMSI retrieved from the SIM card.
+ *
+ * See RFC4186 & RFC4187 & RFC5448 for EAP SIM protocols.
+ *
+ * Identity string is built from IMSI. Format is:
+ * eapPrefix | IMSI | '@' | realm
+ * where:
+ * - "|" denotes concatenation
+ * - realm is the 3GPP network domain name derived from the given
+ * MCC/MNC according to the 3GGP spec(TS23.003)
+ *
+ * eapPrefix value:
+ * '0' - EAP-AKA Identity
+ * '1' - EAP-SIM Identity
+ * '6' - EAP-AKA-PRIME Identity
+ */
+ typedef vec<uint8_t> EapSimIdentity;
+
+ /**
+ * Encrypted EAP IMSI Identity to be used for authentication to EAP SIM
+ * networks which supports encrypted IMSI.
+ * The identity must be derived from the IMSI retrieved from the SIM card.
+ * This identity is then encrypted using the public key of the carrier.
+ *
+ * See RFC4186 & RFC4187 & RFC5448 for EAP SIM protocols.
+ * See section 7.1 of RFC 2437 for RSA-OAEP encryption scheme.
+ *
+ * Identity string is built from encrypted IMSI. Format is:
+ * '\0' | Base64{RSA-OAEP-SHA-256(eapPrefix | IMSI)}
+ * | '@' | realm | {',' Key Identifier}
+ * where:
+ * - "|" denotes concatenation
+ * - "{}" denotes an optional value
+ * - realm is the 3GPP network domain name derived from the given
+ * MCC/MNC according to the 3GGP spec(TS23.003)
+ * - Key Identifier is a null-terminated string of the form "<Key>=<Value>"
+ */
+ typedef vec<uint8_t> EapSimEncryptedIdentity;
+
+ /**
+ * Set EAP encrypted IMSI Identity for this network.
+ *
+ * @param identity Identity string built from the encrypted IMSI.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_ARGS_INVALID|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+ */
+ setEapEncryptedImsiIdentity(EapSimEncryptedIdentity identity)
+ generates (SupplicantStatus status);
+
+ /**
+ * Used to send a response to the
+ * |ISupplicantNetworkCallback.onNetworkEapIdentityRequest| request.
+ *
+ * @param identity Identity string containing the IMSI.
+ * @param encryptedIdentity Identity string containing the encrypted IMSI.
+ * @return status Status of the operation.
+ * Possible status codes:
+ * |SupplicantStatusCode.SUCCESS|,
+ * |SupplicantStatusCode.FAILURE_UNKNOWN|,
+ * |SupplicantStatusCode.FAILURE_NETWORK_INVALID|
+ */
+ sendNetworkEapIdentityResponse_1_1(
+ EapSimIdentity identity,
+ EapSimEncryptedIdentity encryptedIdentity)
+ generates (SupplicantStatus status);
+};