Merge "Revert of "Audio: Add XML schema for audio effects configuration"" into oc-dr1-dev am: a06d7c559c
am: 9f4e3ec880
Change-Id: Ida8a981e37718390df3fa013c12b4f3f516a05bb
diff --git a/automotive/Android.bp b/automotive/Android.bp
index be58829..aec8865 100644
--- a/automotive/Android.bp
+++ b/automotive/Android.bp
@@ -1,6 +1,7 @@
// This is an autogenerated file, do not edit.
subdirs = [
"evs/1.0",
+ "evs/1.0/default",
"evs/1.0/vts/functional",
"vehicle/2.0",
"vehicle/2.1",
diff --git a/automotive/evs/1.0/default/Android.bp b/automotive/evs/1.0/default/Android.bp
new file mode 100644
index 0000000..2574e86
--- /dev/null
+++ b/automotive/evs/1.0/default/Android.bp
@@ -0,0 +1,31 @@
+cc_binary {
+ name: "android.hardware.automotive.evs@1.0-service",
+ defaults: ["hidl_defaults"],
+ proprietary: true,
+ relative_install_path: "hw",
+ srcs: [
+ "service.cpp",
+ "EvsCamera.cpp",
+ "EvsEnumerator.cpp",
+ "EvsDisplay.cpp"
+ ],
+ init_rc: ["android.hardware.automotive.evs@1.0-service.rc"],
+
+ shared_libs: [
+ "android.hardware.automotive.evs@1.0",
+ "libui",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhardware",
+ "libhidlbase",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/automotive/evs/1.0/default/Android.mk b/automotive/evs/1.0/default/Android.mk
deleted file mode 100644
index 0ee7071..0000000
--- a/automotive/evs/1.0/default/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:=$(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.automotive.evs@1.0-service
-LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-service.rc
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_VENDOR_MODULE := true
-
-LOCAL_SRC_FILES := \
- service.cpp \
- EvsCamera.cpp \
- EvsEnumerator.cpp \
- EvsDisplay.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- android.hardware.automotive.evs@1.0 \
- libui \
- libbase \
- libbinder \
- libcutils \
- libhardware \
- libhidlbase \
- libhidltransport \
- liblog \
- libutils \
-
-LOCAL_CFLAGS := -O0 -g
-
-include $(BUILD_EXECUTABLE)
diff --git a/automotive/evs/1.0/vts/functional/VtsEvsV1_0TargetTest.cpp b/automotive/evs/1.0/vts/functional/VtsEvsV1_0TargetTest.cpp
index 2e80afe..57050d7 100644
--- a/automotive/evs/1.0/vts/functional/VtsEvsV1_0TargetTest.cpp
+++ b/automotive/evs/1.0/vts/functional/VtsEvsV1_0TargetTest.cpp
@@ -17,8 +17,13 @@
#define LOG_TAG "VtsHalEvsTest"
-// TODO: How should we configure these values to target appropriate hardware?
-const static char kEnumeratorName[] = "EvsEnumeratorHw-Mock";
+// Note: We have't got a great way to indicate which target
+// should be tested, so we'll leave the interface served by the
+// default (mock) EVS driver here for easy reference. All
+// actual EVS drivers should serve on the EvsEnumeratorHw name,
+// however, so the code is checked in that way.
+//const static char kEnumeratorName[] = "EvsEnumeratorHw-Mock";
+const static char kEnumeratorName[] = "EvsEnumeratorHw";
// These values are called out in the EVS design doc (as of Mar 8, 2017)
@@ -474,4 +479,4 @@
// Explicitly release the display
pEnumerator->closeDisplay(pDisplay);
-}
\ No newline at end of file
+}
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 fe34a3f..4800cd8 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
@@ -61,10 +61,11 @@
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
if (propValue.prop == kGenerateFakeDataControllingProperty) {
- return handleGenerateFakeDataRequest(propValue);
- };
-
- if (mHvacPowerProps.count(propValue.prop)) {
+ StatusCode status = handleGenerateFakeDataRequest(propValue);
+ if (status != StatusCode::OK) {
+ return status;
+ }
+ } else if (mHvacPowerProps.count(propValue.prop)) {
auto hvacPowerOn = mPropStore->readValueOrNull(toInt(VehicleProperty::HVAC_POWER_ON),
toInt(VehicleAreaZone::ROW_1));
@@ -176,6 +177,13 @@
}
bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
+ if (propValue.prop == kGenerateFakeDataControllingProperty) {
+ StatusCode status = handleGenerateFakeDataRequest(propValue);
+ if (status != StatusCode::OK) {
+ return false;
+ }
+ }
+
if (mPropStore->writeValue(propValue)) {
doHalEvent(getValuePool()->obtain(propValue));
return true;
diff --git a/automotive/vehicle/2.1/default/impl/vhal_v2_1/EmulatedVehicleHal.cpp b/automotive/vehicle/2.1/default/impl/vhal_v2_1/EmulatedVehicleHal.cpp
index 46e062b..4faccbd 100644
--- a/automotive/vehicle/2.1/default/impl/vhal_v2_1/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.1/default/impl/vhal_v2_1/EmulatedVehicleHal.cpp
@@ -189,6 +189,7 @@
timestamps.push_back(freezeFrame.timestamp);
}
outValue->value.int64Values = timestamps;
+ outValue->prop = OBD2_FREEZE_FRAME_INFO;
return V2_0::StatusCode::OK;
}
diff --git a/automotive/vehicle/2.1/types.hal b/automotive/vehicle/2.1/types.hal
index 7be611c..54e87ed 100644
--- a/automotive/vehicle/2.1/types.hal
+++ b/automotive/vehicle/2.1/types.hal
@@ -45,6 +45,42 @@
| VehicleArea:GLOBAL),
/**
+ * Automatic re-circulation on/off
+ *
+ * IVehicle#set may return StatusCode::NOT_AVAILABLE and IVehicle#get is not
+ * guaranteed to work if HVAC unit is off. See HVAC_POWER_ON property for
+ * details.
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ HVAC_AUTO_RECIRC_ON = (
+ 0x0512
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:BOOLEAN
+ | VehicleArea:ZONE),
+
+ /**
+ * Vehicle Maps Service (VMS) message
+ *
+ * This property uses COMPLEX data to communicate vms messages.
+ *
+ * Its contents are to be interpreted as follows:
+ * the indices defined in VmsMessageIntegerValuesIndex are to be used to
+ * read from int32Values;
+ * bytes is a serialized VMS message as defined in the vms protocol
+ * which is opaque to the framework;
+ *
+ * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+ * @access VehiclePropertyAccess:READ_WRITE
+ */
+ VEHICLE_MAP_SERVICE = (
+ 0x0C00
+ | VehiclePropertyGroup:SYSTEM
+ | VehiclePropertyType:COMPLEX
+ | VehicleArea:GLOBAL),
+
+ /**
* OBD2 Live Sensor Data
*
* This property uses COMPLEX data to send a snapshot of the current (live)
@@ -174,26 +210,6 @@
| VehiclePropertyGroup:SYSTEM
| VehiclePropertyType:COMPLEX
| VehicleArea:GLOBAL),
-
- /**
- * Vehicle Maps Service (VMS) message
- *
- * This property uses COMPLEX data to communicate vms messages.
- *
- * Its contents are to be interpreted as follows:
- * the indices defined in VmsMessageIntegerValuesIndex are to be used to
- * read from int32Values;
- * bytes is a serialized VMS message as defined in the vms protocol
- * which is opaque to the framework;
- *
- * @change_mode VehiclePropertyChangeMode:ON_CHANGE
- * @access VehiclePropertyAccess:READ_WRITE
- */
- VEHICLE_MAP_SERVICE = (
- 0x0C00
- | VehiclePropertyGroup:SYSTEM
- | VehiclePropertyType:COMPLEX
- | VehicleArea:GLOBAL),
};
/** The status of a fuel system as described by the OBD2 specification. */
@@ -585,20 +601,26 @@
/** A client publishes a data packet. */
DATA = 3,
- /* A client declaring layers offering. */
+ /** A client declaring layers offering. */
OFFERING = 4,
- /* Requesting the list of available layers. */
+ /** Requesting the list of available layers. */
AVAILABILITY_REQUEST = 5,
- /* Returning the list of available layers. */
+ /** Returning the list of available layers. */
AVAILABILITY_RESPONSE = 6,
+ /** Sending to the HAL the current list of the available layers. */
+ AVAILABILITY_CHANGE = 7,
+
/** Requesting layers that have subscribers. */
- SUBSCRIPTION_REQUEST = 7,
+ SUBSCRIPTIONS_REQUEST = 8,
/** Returning layers that have subscribers. */
- SUBSCRIPTION_RESPONSE = 8,
+ SUBSCRIPTIONS_RESPONSE = 9,
+
+ /** Sending to the HAL the current list of the subscribed layers. */
+ SUBSCRIPTIONS_CHANGE = 10,
};
/**
@@ -620,6 +642,9 @@
/* The version of the VMS layer. */
VMS_LAYER_VERSION = 2,
+
+ /* The layer type as defined in the vms protocol */
+ VMS_LAYER_SUB_TYPE = 3,
};
/*
@@ -627,11 +652,14 @@
* value properties
*/
enum VmsOfferingMessageIntegerValuesIndex : VmsBaseMessageIntegerValuesIndex {
+ /* The ID assigend to the publisher by the VMS core. */
+ PUBLISHER_ID = 1,
+
/* The number of VMS layer dependencies. */
- VMS_NUMBER_OF_LAYERS_DEPENDENCIES = 1,
+ VMS_NUMBER_OF_LAYERS_DEPENDENCIES = 2,
/* The first index that contain dependencies */
- FIRST_DEPENDENCIES_INDEX = 2,
+ FIRST_DEPENDENCIES_INDEX = 3,
};
/**
diff --git a/broadcastradio/1.0/types.hal b/broadcastradio/1.0/types.hal
index 045231d..d165e32 100644
--- a/broadcastradio/1.0/types.hal
+++ b/broadcastradio/1.0/types.hal
@@ -155,7 +155,7 @@
enum MetadataKey : int32_t {
INVALID = -1,
- /** RDS PI - string */
+ /** RDS PI - int32_t */
RDS_PI = 0,
/** RDS PS - string */
RDS_PS = 1,
@@ -208,7 +208,10 @@
struct ProgramInfo {
uint32_t channel; /** current channel. (e.g kHz for band type AM_FM) */
uint32_t subChannel; /** current sub channel. (FM_HD) */
- bool tuned; /** tuned to a program or not */
+
+ /** Tuned to a program (not a noise). It's the same condition that would stop scan operation. */
+ bool tuned;
+
bool stereo; /** program is stereo or not */
bool digital; /** digital program or not (e.g HD Radio program) */
uint32_t signalStrength; /** signal strength from 0 to 100 */
diff --git a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
index ebeadb1..7241ad4 100644
--- a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
+++ b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
@@ -228,6 +228,7 @@
bool getProperties();
bool openTuner();
bool checkAntenna();
+ BandConfig& getBand(unsigned idx);
static const nsecs_t kConnectCallbacktimeoutNs = seconds_to_nanoseconds(1);
static const nsecs_t kConfigCallbacktimeoutNs = seconds_to_nanoseconds(10);
@@ -237,6 +238,7 @@
bool skipped;
sp<IBroadcastRadio> mRadio;
Properties mHalProperties;
+ bool mHalPropertiesInitialized = false;
sp<ITuner> mTuner;
sp<MyCallback> mTunerCallback;
Mutex mLock;
@@ -280,23 +282,29 @@
bool BroadcastRadioHidlTest::getProperties()
{
- if (mHalProperties.bands.size() == 0) {
- Result halResult = Result::NOT_INITIALIZED;
- Return<void> hidlReturn =
- mRadio->getProperties([&](Result result, const Properties& properties) {
- halResult = result;
- if (result == Result::OK) {
- mHalProperties = properties;
- }
- });
+ if (mHalPropertiesInitialized) return true;
- EXPECT_TRUE(hidlReturn.isOk());
- EXPECT_EQ(Result::OK, halResult);
- EXPECT_EQ(Class::AM_FM, mHalProperties.classId);
- EXPECT_GT(mHalProperties.numTuners, 0u);
+ Result halResult = Result::NOT_INITIALIZED;
+ auto hidlReturn = mRadio->getProperties([&](Result result, const Properties& properties) {
+ halResult = result;
+ if (result == Result::OK) {
+ mHalProperties = properties;
+ }
+ });
+
+ EXPECT_TRUE(hidlReturn.isOk());
+ EXPECT_EQ(Result::OK, halResult);
+ EXPECT_EQ(radioClass, mHalProperties.classId);
+ EXPECT_GT(mHalProperties.numTuners, 0u);
+ if (radioClass == Class::AM_FM) {
EXPECT_GT(mHalProperties.bands.size(), 0u);
}
- return mHalProperties.bands.size() > 0;
+
+ if (hidlReturn.isOk() && halResult == Result::OK) {
+ mHalPropertiesInitialized = true;
+ return true;
+ }
+ return false;
}
bool BroadcastRadioHidlTest::openTuner()
@@ -306,17 +314,18 @@
}
if (mTuner.get() == nullptr) {
Result halResult = Result::NOT_INITIALIZED;
- Return<void> hidlReturn =
- mRadio->openTuner(mHalProperties.bands[0], true, mTunerCallback,
- [&](Result result, const sp<ITuner>& tuner) {
- halResult = result;
- if (result == Result::OK) {
- mTuner = tuner;
- }
- });
+ auto openCb = [&](Result result, const sp<ITuner>& tuner) {
+ halResult = result;
+ if (result == Result::OK) {
+ mTuner = tuner;
+ }
+ };
+ auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb);
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
- EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
+ if (radioClass == Class::AM_FM) {
+ EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
+ }
}
EXPECT_NE(nullptr, mTuner.get());
return nullptr != mTuner.get();
@@ -324,6 +333,8 @@
bool BroadcastRadioHidlTest::checkAntenna()
{
+ if (radioClass != Class::AM_FM) return true;
+
BandConfig halConfig;
Result halResult = Result::NOT_INITIALIZED;
Return<void> hidlReturn =
@@ -337,6 +348,19 @@
return ((halResult == Result::OK) && (halConfig.antennaConnected == true));
}
+BandConfig& BroadcastRadioHidlTest::getBand(unsigned idx) {
+ static BandConfig dummyBandConfig = {};
+ if (radioClass == Class::AM_FM) {
+ EXPECT_GT(mHalProperties.bands.size(), idx);
+ if (mHalProperties.bands.size() > idx) {
+ return mHalProperties.bands[idx];
+ } else {
+ return dummyBandConfig;
+ }
+ } else {
+ return dummyBandConfig;
+ }
+}
/**
* Test IBroadcastRadio::getProperties() method
@@ -344,7 +368,7 @@
* Verifies that:
* - the HAL implements the method
* - the method returns 0 (no error)
- * - the implementation class is AM_FM
+ * - the implementation class is radioClass
* - the implementation supports at least one tuner
* - the implementation supports at one band
*/
@@ -383,22 +407,20 @@
* Test IBroadcastRadio::openTuner() method called twice.
*
* Verifies that:
- * - the openTuner method fails when called for the second time without deleting previous
- * ITuner instance
+ * - the openTuner method fails with INVALID_STATE or succeeds when called for the second time
+ * without deleting previous ITuner instance
*/
TEST_P(BroadcastRadioHidlTest, OpenTunerTwice) {
RETURN_IF_SKIPPED;
EXPECT_TRUE(openTuner());
Result halResult = Result::NOT_INITIALIZED;
- Return<void> hidlReturn =
- mRadio->openTuner(mHalProperties.bands[0], true, mTunerCallback,
- [&](Result result, const sp<ITuner>&) {
- halResult = result;
- });
+ auto openCb = [&](Result result, const sp<ITuner>&) { halResult = result; };
+ auto hidlReturn = mRadio->openTuner(getBand(0), true, mTunerCallback, openCb);
EXPECT_TRUE(hidlReturn.isOk());
- EXPECT_EQ(Result::INVALID_STATE, halResult);
- EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
+ if (halResult == Result::INVALID_STATE) {
+ EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
+ }
}
/**
@@ -411,16 +433,17 @@
* - the configuration read back from HAl has the same class Id
*/
TEST_P(BroadcastRadioHidlTest, SetAndGetConfiguration) {
+ if (radioClass != Class::AM_FM) skipped = true;
RETURN_IF_SKIPPED;
ASSERT_EQ(true, openTuner());
// test setConfiguration
mCallbackCalled = false;
- Return<Result> hidlResult = mTuner->setConfiguration(mHalProperties.bands[1]);
+ Return<Result> hidlResult = mTuner->setConfiguration(getBand(1));
EXPECT_TRUE(hidlResult.isOk());
EXPECT_EQ(Result::OK, hidlResult);
EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
EXPECT_EQ(Result::OK, mResultCallbackData);
- EXPECT_EQ(mHalProperties.bands[1], mBandConfigCallbackData);
+ EXPECT_EQ(getBand(1), mBandConfigCallbackData);
// test getConfiguration
BandConfig halConfig;
@@ -434,7 +457,7 @@
});
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
- EXPECT_EQ(mHalProperties.bands[1], halConfig);
+ EXPECT_EQ(getBand(1), halConfig);
}
/**
@@ -445,6 +468,7 @@
* - the method recovers and succeeds after passing correct arguments
*/
TEST_P(BroadcastRadioHidlTest, SetConfigurationFails) {
+ if (radioClass != Class::AM_FM) skipped = true;
RETURN_IF_SKIPPED;
ASSERT_EQ(true, openTuner());
@@ -463,7 +487,7 @@
// Test setConfiguration recovering after passing good data.
mCallbackCalled = false;
- setResult = mTuner->setConfiguration(mHalProperties.bands[0]);
+ setResult = mTuner->setConfiguration(getBand(0));
EXPECT_TRUE(setResult.isOk());
EXPECT_EQ(Result::OK, setResult);
EXPECT_EQ(true, waitForCallback(kConfigCallbacktimeoutNs));
@@ -508,6 +532,7 @@
* - skipping sub-channel or not does not fail the call
*/
TEST_P(BroadcastRadioHidlTest, Step) {
+ if (radioClass != Class::AM_FM) skipped = true;
RETURN_IF_SKIPPED;
ASSERT_EQ(true, openTuner());
ASSERT_TRUE(checkAntenna());
@@ -535,18 +560,21 @@
* - the tuned callback is received within kTuneCallbacktimeoutNs ns after tune()
*/
TEST_P(BroadcastRadioHidlTest, TuneAndGetProgramInformationAndCancel) {
+ if (radioClass != Class::AM_FM) skipped = true;
RETURN_IF_SKIPPED;
ASSERT_EQ(true, openTuner());
ASSERT_TRUE(checkAntenna());
+ auto& band = getBand(0);
+
// test tune
- ASSERT_GT(mHalProperties.bands[0].spacings.size(), 0u);
- ASSERT_GT(mHalProperties.bands[0].upperLimit, mHalProperties.bands[0].lowerLimit);
+ ASSERT_GT(band.spacings.size(), 0u);
+ ASSERT_GT(band.upperLimit, band.lowerLimit);
// test scan UP
- uint32_t lowerLimit = mHalProperties.bands[0].lowerLimit;
- uint32_t upperLimit = mHalProperties.bands[0].upperLimit;
- uint32_t spacing = mHalProperties.bands[0].spacings[0];
+ uint32_t lowerLimit = band.lowerLimit;
+ uint32_t upperLimit = band.upperLimit;
+ uint32_t spacing = band.spacings[0];
uint32_t channel =
lowerLimit + (((upperLimit - lowerLimit) / 2 + spacing - 1) / spacing) * spacing;
@@ -571,11 +599,8 @@
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
if (mResultCallbackData == Result::OK) {
- EXPECT_EQ(true, halInfo.tuned);
EXPECT_LE(halInfo.channel, upperLimit);
EXPECT_GE(halInfo.channel, lowerLimit);
- } else {
- EXPECT_EQ(false, halInfo.tuned);
}
// test cancel
@@ -593,6 +618,7 @@
* - the method recovers and succeeds after passing correct arguments
*/
TEST_P(BroadcastRadioHidlTest, TuneFailsOutOfBounds) {
+ if (radioClass != Class::AM_FM) skipped = true;
RETURN_IF_SKIPPED;
ASSERT_TRUE(openTuner());
ASSERT_TRUE(checkAntenna());
diff --git a/broadcastradio/1.1/ITuner.hal b/broadcastradio/1.1/ITuner.hal
index 7511629..912786a 100644
--- a/broadcastradio/1.1/ITuner.hal
+++ b/broadcastradio/1.1/ITuner.hal
@@ -21,6 +21,27 @@
interface ITuner extends @1.0::ITuner {
/**
+ * Tune to a specified program.
+ *
+ * For AM/FM, it must be called when a valid configuration has been applied.
+ * Automatically cancels pending scan, step or tune.
+ *
+ * If method returns OK, ITunerCallback.tuneComplete_1_1() MUST be called:
+ * - once successfully tuned;
+ * - after a time out;
+ * - after a full band scan, if no station found.
+ *
+ * The tuned field of ProgramInfo should indicate if tuned to a valid
+ * station or not.
+ *
+ * @param program Program to tune to.
+ * @return result OK if successfully started tunning.
+ * INVALID_ARGUMENTS if invalid arguments are passed.
+ * NOT_INITIALIZED if another error occurs.
+ */
+ tune_1_1(ProgramSelector program) generates (Result result);
+
+ /**
* Retrieve current station information.
* @return result OK if scan successfully started
* NOT_INITIALIZED if another error occurs
diff --git a/broadcastradio/1.1/default/Android.bp b/broadcastradio/1.1/default/Android.bp
index 759eb09..bec8524 100644
--- a/broadcastradio/1.1/default/Android.bp
+++ b/broadcastradio/1.1/default/Android.bp
@@ -27,16 +27,18 @@
"BroadcastRadio.cpp",
"BroadcastRadioFactory.cpp",
"Tuner.cpp",
- "Utils.cpp",
+ "VirtualProgram.cpp",
+ "VirtualRadio.cpp",
+ ],
+ static_libs: [
+ "android.hardware.broadcastradio@1.1-utils-lib",
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog",
- "libhardware",
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
- "libradio_metadata",
],
}
diff --git a/broadcastradio/1.1/default/BroadcastRadio.cpp b/broadcastradio/1.1/default/BroadcastRadio.cpp
index 68c9b93..3aac127 100644
--- a/broadcastradio/1.1/default/BroadcastRadio.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadio.cpp
@@ -13,14 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "BroadcastRadio"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
+#define LOG_TAG "BroadcastRadioDefault.module"
+#define LOG_NDEBUG 0
#include "BroadcastRadio.h"
-#include "Tuner.h"
-#include "Utils.h"
+
+#include <log/log.h>
namespace android {
namespace hardware {
@@ -28,117 +26,126 @@
namespace V1_1 {
namespace implementation {
-using ::android::sp;
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Class;
+using V1_0::Deemphasis;
+using V1_0::Rds;
+
+using std::lock_guard;
+using std::map;
+using std::mutex;
+using std::vector;
+
+// clang-format off
+static const map<Class, ModuleConfig> gModuleConfigs{
+ {Class::AM_FM, ModuleConfig({
+ "Digital radio mock",
+ { // amFmBands
+ AmFmBandConfig({
+ Band::AM_HD,
+ 540, // lowerLimit
+ 1610, // upperLimit
+ 10, // spacing
+ }),
+ AmFmBandConfig({
+ Band::FM_HD,
+ 87900, // lowerLimit
+ 107900, // upperLimit
+ 200, // spacing
+ }),
+ },
+ })},
+
+ {Class::SAT, ModuleConfig({
+ "Satellite radio mock",
+ {}, // amFmBands
+ })},
+};
+// clang-format on
BroadcastRadio::BroadcastRadio(Class classId)
- : mStatus(Result::NOT_INITIALIZED), mClassId(classId), mHwDevice(NULL)
-{
+ : mClassId(classId), mConfig(gModuleConfigs.at(classId)) {}
+
+bool BroadcastRadio::isSupported(Class classId) {
+ return gModuleConfigs.find(classId) != gModuleConfigs.end();
}
-BroadcastRadio::~BroadcastRadio()
-{
- if (mHwDevice != NULL) {
- radio_hw_device_close(mHwDevice);
- }
+Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ return getProperties_1_1(
+ [&](const Properties& properties) { _hidl_cb(Result::OK, properties.base); });
}
-void BroadcastRadio::onFirstRef()
-{
- const hw_module_t *mod;
- int rc;
- ALOGI("%s mClassId %d", __FUNCTION__, mClassId);
+Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ Properties prop11 = {};
+ auto& prop10 = prop11.base;
- mHwDevice = NULL;
- const char *classString = Utils::getClassString(mClassId);
- if (classString == NULL) {
- ALOGE("invalid class ID %d", mClassId);
- mStatus = Result::INVALID_ARGUMENTS;
- return;
+ prop10.classId = mClassId;
+ prop10.implementor = "Google";
+ prop10.product = mConfig.productName;
+ prop10.numTuners = 1;
+ prop10.numAudioSources = 1;
+ prop10.supportsCapture = false;
+ prop11.supportsBackgroundScanning = false;
+ prop11.vendorExension = "dummy";
+
+ prop10.bands.resize(mConfig.amFmBands.size());
+ for (size_t i = 0; i < mConfig.amFmBands.size(); i++) {
+ auto& src = mConfig.amFmBands[i];
+ auto& dst = prop10.bands[i];
+
+ dst.type = src.type;
+ dst.antennaConnected = true;
+ dst.lowerLimit = src.lowerLimit;
+ dst.upperLimit = src.upperLimit;
+ dst.spacings = vector<uint32_t>({src.spacing});
+
+ if (src.type == Band::AM) {
+ dst.ext.am.stereo = true;
+ } else if (src.type == Band::FM) {
+ dst.ext.fm.deemphasis = Deemphasis::D75;
+ dst.ext.fm.stereo = true;
+ dst.ext.fm.rds = Rds::US;
+ dst.ext.fm.ta = true;
+ dst.ext.fm.af = true;
+ dst.ext.fm.ea = true;
+ }
}
- ALOGI("%s RADIO_HARDWARE_MODULE_ID %s %s",
- __FUNCTION__, RADIO_HARDWARE_MODULE_ID, classString);
-
- rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, classString, &mod);
- if (rc != 0) {
- ALOGE("couldn't load radio module %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, classString, strerror(-rc));
- return;
- }
- rc = radio_hw_device_open(mod, &mHwDevice);
- if (rc != 0) {
- ALOGE("couldn't open radio hw device in %s.%s (%s)",
- RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
- mHwDevice = NULL;
- return;
- }
- if (mHwDevice->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
- ALOGE("wrong radio hw device version %04x", mHwDevice->common.version);
- radio_hw_device_close(mHwDevice);
- mHwDevice = NULL;
- } else {
- mStatus = Result::OK;
- }
-}
-
-int BroadcastRadio::closeHalTuner(const struct radio_tuner *halTuner)
-{
- ALOGV("%s", __FUNCTION__);
- if (mHwDevice == NULL) {
- return -ENODEV;
- }
- if (halTuner == 0) {
- return -EINVAL;
- }
- return mHwDevice->close_tuner(mHwDevice, halTuner);
-}
-
-
-// Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
-Return<void> BroadcastRadio::getProperties(getProperties_cb _hidl_cb)
-{
- int rc;
- radio_hal_properties_t halProperties;
- Properties properties;
-
- if (mHwDevice == NULL) {
- rc = -ENODEV;
- goto exit;
- }
- rc = mHwDevice->get_properties(mHwDevice, &halProperties);
- if (rc == 0) {
- Utils::convertPropertiesFromHal(&properties, &halProperties);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), properties);
+ _hidl_cb(prop11);
return Void();
}
-Return<void> BroadcastRadio::getProperties_1_1(getProperties_1_1_cb _hidl_cb __unused)
-{
- return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
-}
+Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio __unused,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
-Return<void> BroadcastRadio::openTuner(const BandConfig& config, bool audio,
- const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb)
-{
- sp<Tuner> tunerImpl = new Tuner(callback, this);
-
- radio_hal_band_config_t halConfig;
- const struct radio_tuner *halTuner;
- Utils::convertBandConfigToHal(&halConfig, &config);
- int rc = mHwDevice->open_tuner(mHwDevice, &halConfig, audio, Tuner::callback,
- tunerImpl.get(), &halTuner);
- if (rc == 0) {
- tunerImpl->setHalTuner(halTuner);
+ auto oldTuner = mTuner.promote();
+ if (oldTuner != nullptr) {
+ ALOGI("Force-closing previously opened tuner");
+ oldTuner->forceClose();
+ mTuner = nullptr;
}
- _hidl_cb(Utils::convertHalResult(rc), tunerImpl);
+ sp<Tuner> newTuner = new Tuner(callback);
+ mTuner = newTuner;
+ if (mClassId == Class::AM_FM) {
+ auto ret = newTuner->setConfiguration(config);
+ if (ret != Result::OK) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {});
+ return Void();
+ }
+ }
+
+ _hidl_cb(Result::OK, newTuner);
return Void();
}
-} // namespace implementation
+} // namespace implementation
} // namespace V1_1
} // namespace broadcastradio
} // namespace hardware
diff --git a/broadcastradio/1.1/default/BroadcastRadio.h b/broadcastradio/1.1/default/BroadcastRadio.h
index 7de31a0..d98afa7 100644
--- a/broadcastradio/1.1/default/BroadcastRadio.h
+++ b/broadcastradio/1.1/default/BroadcastRadio.h
@@ -16,9 +16,10 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIO_H
+#include "Tuner.h"
+
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/types.h>
-#include <hardware/radio.h>
namespace android {
namespace hardware {
@@ -26,42 +27,48 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Class;
-using V1_0::BandConfig;
-using V1_0::Properties;
+struct AmFmBandConfig {
+ V1_0::Band type;
+ uint32_t lowerLimit; // kHz
+ uint32_t upperLimit; // kHz
+ uint32_t spacing; // kHz
+};
+
+struct ModuleConfig {
+ std::string productName;
+ std::vector<AmFmBandConfig> amFmBands;
+};
struct BroadcastRadio : public V1_1::IBroadcastRadio {
+ /**
+ * Constructs new broadcast radio module.
+ *
+ * Before calling a constructor with a given classId, it must be checked with isSupported
+ * method first. Otherwise it results in undefined behaviour.
+ *
+ * @param classId type of a radio.
+ */
+ BroadcastRadio(V1_0::Class classId);
- BroadcastRadio(Class classId);
+ /**
+ * Checks, if a given radio type is supported.
+ *
+ * @param classId type of a radio.
+ */
+ static bool isSupported(V1_0::Class classId);
- // Methods from ::android::hardware::broadcastradio::V1_1::IBroadcastRadio follow.
+ // V1_1::IBroadcastRadio methods
Return<void> getProperties(getProperties_cb _hidl_cb) override;
Return<void> getProperties_1_1(getProperties_1_1_cb _hidl_cb) override;
- Return<void> openTuner(const BandConfig& config, bool audio,
- const sp<V1_0::ITunerCallback>& callback, openTuner_cb _hidl_cb) override;
+ Return<void> openTuner(const V1_0::BandConfig& config, bool audio,
+ const sp<V1_0::ITunerCallback>& callback,
+ openTuner_cb _hidl_cb) override;
- // RefBase
- virtual void onFirstRef() override;
-
- Result initCheck() { return mStatus; }
- int closeHalTuner(const struct radio_tuner *halTuner);
-
-private:
- virtual ~BroadcastRadio();
-
- static const char * sClassModuleNames[];
-
- Result convertHalResult(int rc);
- void convertBandConfigFromHal(BandConfig *config,
- const radio_hal_band_config_t *halConfig);
- void convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties);
- void convertBandConfigToHal(radio_hal_band_config_t *halConfig,
- const BandConfig *config);
-
- Result mStatus;
- Class mClassId;
- struct radio_hw_device *mHwDevice;
+ private:
+ std::mutex mMut;
+ V1_0::Class mClassId;
+ ModuleConfig mConfig;
+ wp<Tuner> mTuner;
};
} // namespace implementation
diff --git a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
index c8b6c39..c2c1158 100644
--- a/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
+++ b/broadcastradio/1.1/default/BroadcastRadioFactory.cpp
@@ -13,29 +13,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "BroadcastRadioDefault.factory"
+#define LOG_NDEBUG 0
+
#include "BroadcastRadioFactory.h"
+
#include "BroadcastRadio.h"
+#include <log/log.h>
+
namespace android {
namespace hardware {
namespace broadcastradio {
namespace V1_1 {
namespace implementation {
-// Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
-Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
- sp<BroadcastRadio> impl = new BroadcastRadio(classId);
- Result retval = Result::NOT_INITIALIZED;
- if (impl != 0) {
- retval = impl->initCheck();
- }
- _hidl_cb(retval, impl);
- return Void();
+using V1_0::Class;
+
+using std::vector;
+
+static const vector<Class> gAllClasses = {
+ Class::AM_FM, Class::SAT, Class::DT,
+};
+
+IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name __unused) {
+ return new BroadcastRadioFactory();
}
+BroadcastRadioFactory::BroadcastRadioFactory() {
+ for (auto&& classId : gAllClasses) {
+ if (!BroadcastRadio::isSupported(classId)) continue;
+ mRadioModules[classId] = new BroadcastRadio(classId);
+ }
+}
-IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* /* name */) {
- return new BroadcastRadioFactory();
+Return<void> BroadcastRadioFactory::connectModule(Class classId, connectModule_cb _hidl_cb) {
+ ALOGV("%s", __func__);
+
+ auto moduleIt = mRadioModules.find(classId);
+ if (moduleIt == mRadioModules.end()) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
+ } else {
+ _hidl_cb(Result::OK, moduleIt->second);
+ }
+
+ return Void();
}
} // namespace implementation
diff --git a/broadcastradio/1.1/default/BroadcastRadioFactory.h b/broadcastradio/1.1/default/BroadcastRadioFactory.h
index 8eb8514..8b67ac3 100644
--- a/broadcastradio/1.1/default/BroadcastRadioFactory.h
+++ b/broadcastradio/1.1/default/BroadcastRadioFactory.h
@@ -16,6 +16,7 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_BROADCASTRADIOFACTORY_H
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
#include <android/hardware/broadcastradio/1.1/types.h>
@@ -25,14 +26,17 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Class;
+extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);
struct BroadcastRadioFactory : public IBroadcastRadioFactory {
- // Methods from ::android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory follow.
- Return<void> connectModule(Class classId, connectModule_cb _hidl_cb) override;
-};
+ BroadcastRadioFactory();
-extern "C" IBroadcastRadioFactory* HIDL_FETCH_IBroadcastRadioFactory(const char* name);
+ // V1_0::IBroadcastRadioFactory methods
+ Return<void> connectModule(V1_0::Class classId, connectModule_cb _hidl_cb) override;
+
+ private:
+ std::map<V1_0::Class, sp<IBroadcastRadio>> mRadioModules;
+};
} // namespace implementation
} // namespace V1_1
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp
index ae5848c..bb07f36 100644
--- a/broadcastradio/1.1/default/Tuner.cpp
+++ b/broadcastradio/1.1/default/Tuner.cpp
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-#define LOG_TAG "Tuner"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
+#define LOG_TAG "BroadcastRadioDefault.tuner"
+#define LOG_NDEBUG 0
#include "BroadcastRadio.h"
#include "Tuner.h"
-#include "Utils.h"
-#include <system/RadioMetadataWrapper.h>
+
+#include <Utils.h>
+#include <log/log.h>
namespace android {
namespace hardware {
@@ -30,199 +29,291 @@
namespace V1_1 {
namespace implementation {
-void Tuner::onCallback(radio_hal_event_t *halEvent)
-{
- BandConfig config;
- ProgramInfo info;
- hidl_vec<MetaData> metadata;
+using namespace std::chrono_literals;
- if (mCallback != 0) {
- switch(halEvent->type) {
- case RADIO_EVENT_CONFIG:
- Utils::convertBandConfigFromHal(&config, &halEvent->config);
- mCallback->configChange(Utils::convertHalResult(halEvent->status), config);
- break;
- case RADIO_EVENT_ANTENNA:
- mCallback->antennaStateChange(halEvent->on);
- break;
- case RADIO_EVENT_TUNED:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->tuneComplete_1_1(Utils::convertHalResult(halEvent->status), info);
- }
- mCallback->tuneComplete(Utils::convertHalResult(halEvent->status), info.base);
- break;
- case RADIO_EVENT_METADATA: {
- uint32_t channel;
- uint32_t sub_channel;
- if (radio_metadata_get_channel(halEvent->metadata, &channel, &sub_channel) == 0) {
- Utils::convertMetaDataFromHal(metadata, halEvent->metadata);
- mCallback->newMetadata(channel, sub_channel, metadata);
- }
- } break;
- case RADIO_EVENT_TA:
- mCallback->trafficAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_AF_SWITCH:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->afSwitch_1_1(info);
- }
- mCallback->afSwitch(info.base);
- break;
- case RADIO_EVENT_EA:
- mCallback->emergencyAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_HW_FAILURE:
- default:
- mCallback->hardwareFailure();
- break;
- }
- }
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Direction;
+
+using std::chrono::milliseconds;
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::sort;
+using std::vector;
+
+const struct {
+ milliseconds config = 50ms;
+ milliseconds scan = 200ms;
+ milliseconds step = 100ms;
+ milliseconds tune = 150ms;
+} gDefaultDelay;
+
+Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback)
+ : mCallback(callback),
+ mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)),
+ mVirtualFm(make_fm_radio()) {}
+
+void Tuner::forceClose() {
+ lock_guard<mutex> lk(mMut);
+ mIsClosed = true;
+ mThread.cancelAll();
}
-//static
-void Tuner::callback(radio_hal_event_t *halEvent, void *cookie)
-{
- wp<Tuner> weak(reinterpret_cast<Tuner*>(cookie));
- sp<Tuner> tuner = weak.promote();
- if (tuner == 0) return;
- tuner->onCallback(halEvent);
+Return<Result> Tuner::setConfiguration(const BandConfig& config) {
+ ALOGV("%s", __func__);
+
+ if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
+
+ auto task = [this, config]() {
+ ALOGI("Setting AM/FM config");
+ lock_guard<mutex> lk(mMut);
+
+ mAmfmConfig = move(config);
+ mAmfmConfig.antennaConnected = true;
+ mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
+
+ mIsAmfmConfigSet = true;
+ mCallback->configChange(Result::OK, mAmfmConfig);
+ };
+ mThread.schedule(task, gDefaultDelay.config);
+
+ return Result::OK;
}
-Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback, const wp<BroadcastRadio>& parentDevice)
- : mHalTuner(NULL), mCallback(callback), mCallback1_1(ITunerCallback::castFrom(callback)),
- mParentDevice(parentDevice)
-{
- ALOGV("%s", __FUNCTION__);
-}
+Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
+ ALOGV("%s", __func__);
-
-Tuner::~Tuner()
-{
- ALOGV("%s", __FUNCTION__);
- const sp<BroadcastRadio> parentDevice = mParentDevice.promote();
- if (parentDevice != 0) {
- parentDevice->closeHalTuner(mHalTuner);
+ lock_guard<mutex> lk(mMut);
+ if (mIsAmfmConfigSet) {
+ _hidl_cb(Result::OK, mAmfmConfig);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, {});
}
-}
-
-// Methods from ::android::hardware::broadcastradio::V1_1::ITuner follow.
-Return<Result> Tuner::setConfiguration(const BandConfig& config) {
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- radio_hal_band_config_t halConfig;
- Utils::convertBandConfigToHal(&halConfig, &config);
- int rc = mHalTuner->set_configuration(mHalTuner, &halConfig);
- return Utils::convertHalResult(rc);
-}
-
-Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
- int rc;
- radio_hal_band_config_t halConfig;
- BandConfig config;
-
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
- }
- rc = mHalTuner->get_configuration(mHalTuner, &halConfig);
- if (rc == 0) {
- Utils::convertBandConfigFromHal(&config, &halConfig);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), config);
return Void();
}
-Return<Result> Tuner::scan(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- int rc = mHalTuner->scan(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+// makes ProgramInfo that points to no program
+static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ utils::getLegacyChannel(selector, info10.channel, info10.subChannel);
+ info11.selector = selector;
+ info11.flags |= ProgramInfoFlags::MUTED;
+
+ return info11;
}
-Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
+ VirtualRadio* virtualRadio = nullptr;
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ virtualRadio = &mVirtualFm;
}
- int rc = mHalTuner->step(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+
+ VirtualProgram virtualProgram;
+ if (virtualRadio != nullptr && virtualRadio->getProgram(sel, virtualProgram)) {
+ mCurrentProgram = virtualProgram.selector;
+ mCurrentProgramInfo = static_cast<ProgramInfo>(virtualProgram);
+ } else {
+ mCurrentProgram = sel;
+ mCurrentProgramInfo = makeDummyProgramInfo(sel);
+ }
+ mIsTuneCompleted = true;
+
+ mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base);
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo);
+ }
}
-Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ vector<VirtualProgram> list;
+
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ list = mVirtualFm.getProgramList();
}
- int rc = mHalTuner->tune(mHalTuner, channel, subChannel);
- return Utils::convertHalResult(rc);
+
+ if (list.size() == 0) {
+ mIsTuneCompleted = false;
+ auto task = [this, direction]() {
+ ALOGI("Performing failed scan %s", toString(direction).c_str());
+
+ mCallback->tuneComplete(Result::TIMEOUT, {});
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
+ }
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+ }
+
+ // Not optimal (O(sort) instead of O(n)), but not a big deal here;
+ // also, it's likely that list is already sorted (so O(n) anyway).
+ sort(list.begin(), list.end());
+ auto current = mCurrentProgram;
+ auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
+ if (direction == Direction::UP) {
+ if (found < list.end() - 1) {
+ if (utils::tunesTo(current, found->selector)) found++;
+ } else {
+ found = list.begin();
+ }
+ } else {
+ if (found > list.begin() && found != list.end()) {
+ found--;
+ } else {
+ found = list.end() - 1;
+ }
+ }
+ auto tuneTo = found->selector;
+
+ mIsTuneCompleted = false;
+ auto task = [this, tuneTo, direction]() {
+ ALOGI("Performing scan %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+ tuneInternalLocked(tuneTo);
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
}
-Return<Result> Tuner::cancel() {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
+ ALOGV("%s", __func__);
+ ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
+
+ lock_guard<mutex> lk(mMut);
+
+ if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
+ ALOGE("Can't step in anything else than AM/FM");
+ return Result::NOT_INITIALIZED;
}
- int rc = mHalTuner->cancel(mHalTuner);
- return Utils::convertHalResult(rc);
+
+ ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+ if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+ mIsTuneCompleted = false;
+
+ auto task = [this, direction]() {
+ ALOGI("Performing step %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+
+ auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
+
+ if (direction == Direction::UP) {
+ current += mAmfmConfig.spacings[0];
+ } else {
+ current -= mAmfmConfig.spacings[0];
+ }
+
+ if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
+ if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
+
+ tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current));
+ };
+ mThread.schedule(task, gDefaultDelay.step);
+
+ return Result::OK;
}
-Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
- ALOGV("%s", __FUNCTION__);
+Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
+ ALOGV("%s(%d, %d)", __func__, channel, subChannel);
+ Band band;
+ {
+ lock_guard<mutex> lk(mMut);
+ band = mAmfmConfig.type;
+ }
+ return tune_1_1(utils::make_selector(band, channel, subChannel));
+}
+
+Return<Result> Tuner::tune_1_1(const ProgramSelector& sel) {
+ ALOGV("%s(%s)", __func__, toString(sel).c_str());
+
+ lock_guard<mutex> lk(mMut);
+
+ if (utils::isAmFm(utils::getType(mCurrentProgram))) {
+ ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+ if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+
+ auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
+ if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
+ return Result::INVALID_ARGUMENTS;
+ }
+ }
+
+ mIsTuneCompleted = false;
+ auto task = [this, sel]() {
+ lock_guard<mutex> lk(mMut);
+ tuneInternalLocked(sel);
+ };
+ mThread.schedule(task, gDefaultDelay.tune);
+
+ return Result::OK;
+}
+
+Return<Result> Tuner::cancel() {
+ ALOGV("%s", __func__);
+ mThread.cancelAll();
+ return Result::OK;
+}
+
+Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
+ ALOGV("%s", __func__);
return getProgramInformation_1_1([&](Result result, const ProgramInfo& info) {
_hidl_cb(result, info.base);
});
}
-Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
- int rc;
- radio_program_info_t halInfo;
- RadioMetadataWrapper metadataWrapper(&halInfo.metadata);
- ProgramInfo info;
+Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
+ ALOGV("%s", __func__);
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
+ lock_guard<mutex> lk(mMut);
+ if (mIsTuneCompleted) {
+ _hidl_cb(Result::OK, mCurrentProgramInfo);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
}
-
- rc = mHalTuner->get_program_information(mHalTuner, &halInfo);
- if (rc == 0) {
- Utils::convertProgramInfoFromHal(&info, &halInfo);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), info);
return Void();
}
Return<ProgramListResult> Tuner::startBackgroundScan() {
+ ALOGV("%s", __func__);
return ProgramListResult::UNAVAILABLE;
}
Return<void> Tuner::getProgramList(const hidl_string& filter __unused, getProgramList_cb _hidl_cb) {
- hidl_vec<ProgramInfo> pList;
- // TODO(b/34054813): do the actual implementation.
- _hidl_cb(ProgramListResult::NOT_STARTED, pList);
+ ALOGV("%s", __func__);
+
+ auto& virtualRadio = mVirtualFm;
+ if (mAmfmConfig.type != Band::FM_HD && mAmfmConfig.type != Band::FM) {
+ _hidl_cb(ProgramListResult::OK, {});
+ return Void();
+ }
+
+ auto list = virtualRadio.getProgramList();
+ _hidl_cb(ProgramListResult::OK, vector<ProgramInfo>(list.begin(), list.end()));
return Void();
}
Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
_hidl_cb(Result::INVALID_STATE, false);
return Void();
}
Return<Result> Tuner::setAnalogForced(bool isForced __unused) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
return Result::INVALID_STATE;
}
-} // namespace implementation
+} // namespace implementation
} // namespace V1_1
} // namespace broadcastradio
} // namespace hardware
diff --git a/broadcastradio/1.1/default/Tuner.h b/broadcastradio/1.1/default/Tuner.h
index 57eafd3..17ee99f 100644
--- a/broadcastradio/1.1/default/Tuner.h
+++ b/broadcastradio/1.1/default/Tuner.h
@@ -16,6 +16,9 @@
#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_TUNER_H
#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_TUNER_H
+#include "VirtualRadio.h"
+
+#include <WorkerThread.h>
#include <android/hardware/broadcastradio/1.1/ITuner.h>
#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
@@ -25,20 +28,18 @@
namespace V1_1 {
namespace implementation {
-using V1_0::Direction;
-
-struct BroadcastRadio;
-
struct Tuner : public ITuner {
+ Tuner(const sp<V1_0::ITunerCallback>& callback);
- Tuner(const sp<V1_0::ITunerCallback>& callback, const wp<BroadcastRadio>& mParentDevice);
+ void forceClose();
- // Methods from ::android::hardware::broadcastradio::V1_1::ITuner follow.
- Return<Result> setConfiguration(const BandConfig& config) override;
+ // V1_1::ITuner methods
+ Return<Result> setConfiguration(const V1_0::BandConfig& config) override;
Return<void> getConfiguration(getConfiguration_cb _hidl_cb) override;
- Return<Result> scan(Direction direction, bool skipSubChannel) override;
- Return<Result> step(Direction direction, bool skipSubChannel) override;
+ Return<Result> scan(V1_0::Direction direction, bool skipSubChannel) override;
+ Return<Result> step(V1_0::Direction direction, bool skipSubChannel) override;
Return<Result> tune(uint32_t channel, uint32_t subChannel) override;
+ Return<Result> tune_1_1(const ProgramSelector& program) override;
Return<Result> cancel() override;
Return<void> getProgramInformation(getProgramInformation_cb _hidl_cb) override;
Return<void> getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) override;
@@ -47,21 +48,24 @@
Return<void> isAnalogForced(isAnalogForced_cb _hidl_cb) override;
Return<Result> setAnalogForced(bool isForced) override;
- static void callback(radio_hal_event_t *halEvent, void *cookie);
- void onCallback(radio_hal_event_t *halEvent);
+ private:
+ std::mutex mMut;
+ WorkerThread mThread;
+ bool mIsClosed = false; // TODO(b/36864090): use it
- void setHalTuner(const struct radio_tuner *halTuner) { mHalTuner = halTuner; }
- const struct radio_tuner *getHalTuner() { return mHalTuner; }
-
-private:
- ~Tuner();
-
- const struct radio_tuner *mHalTuner;
const sp<V1_0::ITunerCallback> mCallback;
const sp<V1_1::ITunerCallback> mCallback1_1;
- const wp<BroadcastRadio> mParentDevice;
-};
+ VirtualRadio mVirtualFm;
+
+ bool mIsAmfmConfigSet = false;
+ V1_0::BandConfig mAmfmConfig;
+ bool mIsTuneCompleted = false;
+ ProgramSelector mCurrentProgram = {};
+ ProgramInfo mCurrentProgramInfo = {};
+
+ void tuneInternalLocked(const ProgramSelector& sel);
+};
} // namespace implementation
} // namespace V1_1
diff --git a/broadcastradio/1.1/default/Utils.cpp b/broadcastradio/1.1/default/Utils.cpp
deleted file mode 100644
index e21344e..0000000
--- a/broadcastradio/1.1/default/Utils.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define LOG_TAG "BroadcastRadioHalUtils"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
-#include <system/radio_metadata.h>
-
-#include "Utils.h"
-
-namespace android {
-namespace hardware {
-namespace broadcastradio {
-namespace V1_1 {
-namespace implementation {
-
-using V1_0::Band;
-using V1_0::Deemphasis;
-using V1_0::Direction;
-using V1_0::MetadataKey;
-using V1_0::MetadataType;
-using V1_0::Rds;
-
-const char *Utils::sClassModuleNames[] = {
- RADIO_HARDWARE_MODULE_ID_FM, /* corresponds to RADIO_CLASS_AM_FM */
- RADIO_HARDWARE_MODULE_ID_SAT, /* corresponds to RADIO_CLASS_SAT */
- RADIO_HARDWARE_MODULE_ID_DT, /* corresponds to RADIO_CLASS_DT */
-};
-
-// make sure HIDL enum values are aligned with legacy values
-static_assert(RADIO_CLASS_AM_FM == static_cast<int>(Class::AM_FM),
- "AM/FM class mismatch with legacy");
-static_assert(RADIO_CLASS_SAT == static_cast<int>(Class::SAT),
- "SAT class mismatch with legacy");
-static_assert(RADIO_CLASS_DT == static_cast<int>(Class::DT),
- "DT class mismatch with legacy");
-
-static_assert(RADIO_BAND_AM == static_cast<int>(Band::AM),
- "AM band mismatch with legacy");
-static_assert(RADIO_BAND_FM == static_cast<int>(Band::FM),
- "FM band mismatch with legacy");
-static_assert(RADIO_BAND_AM_HD == static_cast<int>(Band::AM_HD),
- "AM HD band mismatch with legacy");
-static_assert(RADIO_BAND_FM_HD == static_cast<int>(Band::FM_HD),
- "FM HD band mismatch with legacy");
-
-static_assert(RADIO_RDS_NONE == static_cast<int>(Rds::NONE),
- "RDS NONE mismatch with legacy");
-static_assert(RADIO_RDS_WORLD == static_cast<int>(Rds::WORLD),
- "RDS WORLD mismatch with legacy");
-static_assert(RADIO_RDS_US == static_cast<int>(Rds::US),
- "RDS US mismatch with legacy");
-
-static_assert(RADIO_DEEMPHASIS_50 == static_cast<int>(Deemphasis::D50),
- "De-emphasis 50 mismatch with legacy");
-static_assert(RADIO_DEEMPHASIS_75 == static_cast<int>(Deemphasis::D75),
- "De-emphasis 75 mismatch with legacy");
-
-static_assert(RADIO_DIRECTION_UP == static_cast<int>(Direction::UP),
- "Direction Up mismatch with legacy");
-static_assert(RADIO_DIRECTION_DOWN == static_cast<int>(Direction::DOWN),
- "Direction Up mismatch with legacy");
-
-static_assert(RADIO_METADATA_TYPE_INVALID == static_cast<int>(MetadataType::INVALID),
- "Metadata type INVALID mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_INT == static_cast<int>(MetadataType::INT),
- "Metadata type INT mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_TEXT == static_cast<int>(MetadataType::TEXT),
- "Metadata type TEXT mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_RAW == static_cast<int>(MetadataType::RAW),
- "Metadata type RAW mismatch with legacy");
-static_assert(RADIO_METADATA_TYPE_CLOCK == static_cast<int>(MetadataType::CLOCK),
- "Metadata type CLOCK mismatch with legacy");
-
-static_assert(RADIO_METADATA_KEY_INVALID == static_cast<int>(MetadataKey::INVALID),
- "Metadata key INVALID mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PI == static_cast<int>(MetadataKey::RDS_PI),
- "Metadata key RDS_PI mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PS == static_cast<int>(MetadataKey::RDS_PS),
- "Metadata key RDS_PS mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_PTY == static_cast<int>(MetadataKey::RDS_PTY),
- "Metadata key RDS_PTY mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RBDS_PTY == static_cast<int>(MetadataKey::RBDS_PTY),
- "Metadata key RBDS_PTY mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_RDS_RT == static_cast<int>(MetadataKey::RDS_RT),
- "Metadata key RDS_RT mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_TITLE == static_cast<int>(MetadataKey::TITLE),
- "Metadata key TITLE mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ARTIST == static_cast<int>(MetadataKey::ARTIST),
- "Metadata key ARTIST mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ALBUM == static_cast<int>(MetadataKey::ALBUM),
- "Metadata key ALBUM mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_GENRE == static_cast<int>(MetadataKey::GENRE),
- "Metadata key GENRE mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ICON == static_cast<int>(MetadataKey::ICON),
- "Metadata key ICON mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_ART == static_cast<int>(MetadataKey::ART),
- "Metadata key ART mismatch with legacy");
-static_assert(RADIO_METADATA_KEY_CLOCK == static_cast<int>(MetadataKey::CLOCK),
- "Metadata key CLOCK mismatch with legacy");
-
-
-//static
-const char * Utils::getClassString(Class ClassId)
-{
- int id = static_cast<int>(ClassId);
-
- if ((id < 0) ||
- (id >= NELEM(sClassModuleNames))) {
- ALOGE("invalid class ID %d", id);
- return NULL;
- }
- return sClassModuleNames[id];
-}
-
-//static
-Result Utils::convertHalResult(int rc)
-{
- switch (rc) {
- case 0:
- return Result::OK;
- case -EINVAL:
- return Result::INVALID_ARGUMENTS;
- case -ENOSYS:
- return Result::INVALID_STATE;
- case -ETIMEDOUT:
- return Result::TIMEOUT;
- case -ENODEV:
- default:
- return Result::NOT_INITIALIZED;
- }
-}
-
-//static
-void Utils::convertBandConfigFromHal(
- BandConfig *config,
- const radio_hal_band_config_t *halConfig)
-{
-
- config->type = static_cast<Band>(halConfig->type);
- config->antennaConnected = halConfig->antenna_connected;
- config->lowerLimit = halConfig->lower_limit;
- config->upperLimit = halConfig->upper_limit;
- config->spacings.setToExternal(const_cast<unsigned int *>(&halConfig->spacings[0]),
- halConfig->num_spacings * sizeof(uint32_t));
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- config->spacings.resize(halConfig->num_spacings);
-
- if (config->type == Band::FM) {
- config->ext.fm.deemphasis = static_cast<Deemphasis>(halConfig->fm.deemphasis);
- config->ext.fm.stereo = halConfig->fm.stereo;
- config->ext.fm.rds = static_cast<Rds>(halConfig->fm.rds);
- config->ext.fm.ta = halConfig->fm.ta;
- config->ext.fm.af = halConfig->fm.af;
- config->ext.fm.ea = halConfig->fm.ea;
- } else {
- config->ext.am.stereo = halConfig->am.stereo;
- }
-}
-
-//static
-void Utils::convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties)
-{
- properties->classId = static_cast<Class>(halProperties->class_id);
- properties->implementor.setToExternal(halProperties->implementor, strlen(halProperties->implementor));
- properties->product.setToExternal(halProperties->product, strlen(halProperties->product));
- properties->version.setToExternal(halProperties->version, strlen(halProperties->version));
- properties->serial.setToExternal(halProperties->serial, strlen(halProperties->serial));
- properties->numTuners = halProperties->num_tuners;
- properties->numAudioSources = halProperties->num_audio_sources;
- properties->supportsCapture = halProperties->supports_capture;
-
- BandConfig *bands =
- new BandConfig[halProperties->num_bands];
- for (size_t i = 0; i < halProperties->num_bands; i++) {
- convertBandConfigFromHal(&bands[i], &halProperties->bands[i]);
- }
- properties->bands.setToExternal(bands, halProperties->num_bands);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- properties->bands.resize(halProperties->num_bands);
- delete[] bands;
-}
-
-//static
-void Utils::convertBandConfigToHal(radio_hal_band_config_t *halConfig, const BandConfig *config)
-{
- halConfig->type = static_cast<radio_band_t>(config->type);
- halConfig->antenna_connected = config->antennaConnected;
- halConfig->lower_limit = config->lowerLimit;
- halConfig->upper_limit = config->upperLimit;
- halConfig->num_spacings = config->spacings.size();
- if (halConfig->num_spacings > RADIO_NUM_SPACINGS_MAX) {
- halConfig->num_spacings = RADIO_NUM_SPACINGS_MAX;
- }
- memcpy(halConfig->spacings, config->spacings.data(),
- sizeof(uint32_t) * halConfig->num_spacings);
-
- if (config->type == Band::FM) {
- halConfig->fm.deemphasis = static_cast<radio_deemphasis_t>(config->ext.fm.deemphasis);
- halConfig->fm.stereo = config->ext.fm.stereo;
- halConfig->fm.rds = static_cast<radio_rds_t>(config->ext.fm.rds);
- halConfig->fm.ta = config->ext.fm.ta;
- halConfig->fm.af = config->ext.fm.af;
- halConfig->fm.ea = config->ext.fm.ea;
- } else {
- halConfig->am.stereo = config->ext.am.stereo;
- }
-}
-
-
-//static
-void Utils::convertProgramInfoFromHal(ProgramInfo *info, radio_program_info_t *halInfo)
-{
- auto &info_1_1 = *info;
- auto &info_1_0 = info->base;
-
- info_1_0.channel = halInfo->channel;
- info_1_0.subChannel = halInfo->sub_channel;
- info_1_0.tuned = halInfo->tuned;
- info_1_0.stereo = halInfo->stereo;
- info_1_0.digital = halInfo->digital;
- info_1_0.signalStrength = halInfo->signal_strength;
- convertMetaDataFromHal(info_1_0.metadata, halInfo->metadata);
- // TODO(b/34348946): add support for HAL 1.1 fields
- info_1_1.flags = 0;
-}
-
-//static
-int Utils::convertMetaDataFromHal(hidl_vec<MetaData>& metadata, radio_metadata_t *halMetadata)
-{
- if (halMetadata == NULL) {
- ALOGE("Invalid argument: halMetadata is NULL");
- return 0;
- }
-
- int count = radio_metadata_get_count(halMetadata);
- if (count <= 0) {
- return count;
- }
- MetaData *newMetadata = new MetaData[count];
- int outCount = 0;
- for (int i = 0; i < count; i++) {
- radio_metadata_key_t key;
- radio_metadata_type_t type;
- void *value;
- size_t size;
- if (radio_metadata_get_at_index(halMetadata, i , &key, &type, &value, &size) != 0 ||
- size == 0) {
- continue;
- }
- switch (type) {
- case RADIO_METADATA_TYPE_INT: {
- newMetadata[outCount].intValue = *(static_cast<int32_t *>(value));
- } break;
- case RADIO_METADATA_TYPE_TEXT: {
- newMetadata[outCount].stringValue = static_cast<char *>(value);
- } break;
- case RADIO_METADATA_TYPE_RAW: {
- newMetadata[outCount].rawValue.setToExternal(static_cast<uint8_t *>(value), size);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- newMetadata[outCount].rawValue.resize(size);
- } break;
- case RADIO_METADATA_TYPE_CLOCK: {
- radio_metadata_clock_t *clock = static_cast<radio_metadata_clock_t *>(value);
- newMetadata[outCount].clockValue.utcSecondsSinceEpoch =
- clock->utc_seconds_since_epoch;
- newMetadata[outCount].clockValue.timezoneOffsetInMinutes =
- clock->timezone_offset_in_minutes;
- } break;
- }
- newMetadata[outCount].type = static_cast<MetadataType>(type);
- newMetadata[outCount].key = static_cast<MetadataKey>(key);
- outCount++;
- }
- metadata.setToExternal(newMetadata, outCount);
- // FIXME: transfer buffer ownership. should have a method for that in hidl_vec
- metadata.resize(outCount);
- return outCount;
-}
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace broadcastradio
-} // namespace hardware
-} // namespace android
diff --git a/broadcastradio/1.1/default/Utils.h b/broadcastradio/1.1/default/Utils.h
deleted file mode 100644
index 22902ba..0000000
--- a/broadcastradio/1.1/default/Utils.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
-#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
-
-#include <android/hardware/broadcastradio/1.1/types.h>
-#include <hardware/radio.h>
-
-namespace android {
-namespace hardware {
-namespace broadcastradio {
-namespace V1_1 {
-namespace implementation {
-
-using V1_0::Class;
-using V1_0::BandConfig;
-using V1_0::MetaData;
-using V1_0::Properties;
-
-class Utils {
-public:
- static const char * getClassString(Class ClassId);
- static Result convertHalResult(int rc);
- static void convertBandConfigFromHal(BandConfig *config,
- const radio_hal_band_config_t *halConfig);
- static void convertPropertiesFromHal(Properties *properties,
- const radio_hal_properties_t *halProperties);
- static void convertBandConfigToHal(radio_hal_band_config_t *halConfig,
- const BandConfig *config);
- static void convertProgramInfoFromHal(ProgramInfo *info,
- radio_program_info_t *halInfo);
- static int convertMetaDataFromHal(hidl_vec<MetaData>& metadata,
- radio_metadata_t *halMetadata);
-private:
- static const char * sClassModuleNames[];
-
-};
-
-} // namespace implementation
-} // namespace V1_1
-} // namespace broadcastradio
-} // namespace hardware
-} // namespace android
-
-#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
diff --git a/broadcastradio/1.1/default/VirtualProgram.cpp b/broadcastradio/1.1/default/VirtualProgram.cpp
new file mode 100644
index 0000000..ef8bd1c
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualProgram.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "VirtualProgram.h"
+
+#include <Utils.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+using V1_0::MetaData;
+using V1_0::MetadataKey;
+using V1_0::MetadataType;
+
+VirtualProgram::operator ProgramInfo() const {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ utils::getLegacyChannel(selector, info10.channel, info10.subChannel);
+ info11.selector = selector;
+ info10.tuned = true;
+ info10.stereo = true;
+ info10.digital = utils::isDigital(selector);
+ info10.signalStrength = info10.digital ? 100 : 80;
+
+ info10.metadata = hidl_vec<MetaData>({
+ {MetadataType::TEXT, MetadataKey::RDS_PS, {}, {}, programName, {}},
+ {MetadataType::TEXT, MetadataKey::TITLE, {}, {}, songTitle, {}},
+ {MetadataType::TEXT, MetadataKey::ARTIST, {}, {}, songArtist, {}},
+ });
+
+ return info11;
+}
+
+// Defining order on virtual programs, how they appear on band.
+// It's mostly for default implementation purposes, may not be complete or correct.
+bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs) {
+ auto& l = lhs.selector;
+ auto& r = rhs.selector;
+
+ // Two programs with the same primaryId is considered the same.
+ if (l.programType != r.programType) return l.programType < r.programType;
+ if (l.primaryId.type != r.primaryId.type) return l.primaryId.type < r.primaryId.type;
+ if (l.primaryId.value != r.primaryId.value) return l.primaryId.value < r.primaryId.value;
+
+ // A little exception for HD Radio subchannel - we check secondary ID too.
+ if (utils::hasId(l, IdentifierType::HD_SUBCHANNEL) &&
+ utils::hasId(r, IdentifierType::HD_SUBCHANNEL)) {
+ return utils::getId(l, IdentifierType::HD_SUBCHANNEL) <
+ utils::getId(r, IdentifierType::HD_SUBCHANNEL);
+ }
+
+ return false;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.1/default/VirtualProgram.h b/broadcastradio/1.1/default/VirtualProgram.h
new file mode 100644
index 0000000..a4fd72c
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualProgram.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
+
+#include <android/hardware/broadcastradio/1.1/types.h>
+#include <cstdint>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+struct VirtualProgram {
+ ProgramSelector selector;
+
+ std::string programName = "";
+ std::string songArtist = "";
+ std::string songTitle = "";
+
+ explicit operator ProgramInfo() const;
+ friend bool operator<(const VirtualProgram& lhs, const VirtualProgram& rhs);
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALPROGRAM_H
diff --git a/broadcastradio/1.1/default/VirtualRadio.cpp b/broadcastradio/1.1/default/VirtualRadio.cpp
new file mode 100644
index 0000000..acf37a4
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualRadio.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "VirtualRadio.h"
+
+#include <Utils.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+using V1_0::Band;
+
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::vector;
+
+using utils::make_selector;
+
+vector<VirtualProgram> gInitialFmPrograms{
+ {make_selector(Band::FM, 94900), "Wild 94.9", "Drake ft. Rihanna", "Too Good"},
+ {make_selector(Band::FM, 96500), "KOIT", "Celine Dion", "All By Myself"},
+ {make_selector(Band::FM, 97300), "Alice@97.3", "Drops of Jupiter", "Train"},
+ {make_selector(Band::FM, 99700), "99.7 Now!", "The Chainsmokers", "Closer"},
+ {make_selector(Band::FM, 101300), "101-3 KISS-FM", "Justin Timberlake", "Rock Your Body"},
+ {make_selector(Band::FM, 103700), "iHeart80s @ 103.7", "Michael Jackson", "Billie Jean"},
+ {make_selector(Band::FM, 106100), "106 KMEL", "Drake", "Marvins Room"},
+};
+
+VirtualRadio::VirtualRadio(VirtualRadio&& o) : mPrograms(move(o.mPrograms)) {}
+
+VirtualRadio::VirtualRadio(vector<VirtualProgram> initialList) : mPrograms(initialList) {}
+
+vector<VirtualProgram> VirtualRadio::getProgramList() {
+ lock_guard<mutex> lk(mMut);
+ return mPrograms;
+}
+
+bool VirtualRadio::getProgram(const ProgramSelector& selector, VirtualProgram& programOut) {
+ lock_guard<mutex> lk(mMut);
+ for (auto&& program : mPrograms) {
+ if (utils::tunesTo(selector, program.selector)) {
+ programOut = program;
+ return true;
+ }
+ }
+ return false;
+}
+
+VirtualRadio make_fm_radio() {
+ return VirtualRadio(gInitialFmPrograms);
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.1/default/VirtualRadio.h b/broadcastradio/1.1/default/VirtualRadio.h
new file mode 100644
index 0000000..23cb06c
--- /dev/null
+++ b/broadcastradio/1.1/default/VirtualRadio.h
@@ -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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
+
+#include "VirtualProgram.h"
+
+#include <mutex>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+
+class VirtualRadio {
+ public:
+ VirtualRadio(VirtualRadio&& o);
+ VirtualRadio(std::vector<VirtualProgram> initialList);
+
+ std::vector<VirtualProgram> getProgramList();
+ bool getProgram(const ProgramSelector& selector, VirtualProgram& program);
+
+ private:
+ std::mutex mMut;
+ std::vector<VirtualProgram> mPrograms;
+};
+
+VirtualRadio make_fm_radio();
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_VIRTUALRADIO_H
diff --git a/broadcastradio/1.1/tests/Android.bp b/broadcastradio/1.1/tests/Android.bp
new file mode 100644
index 0000000..fa1fd94
--- /dev/null
+++ b/broadcastradio/1.1/tests/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "android.hardware.broadcastradio@1.1-utils-tests",
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "WorkerThread_test.cpp",
+ ],
+ static_libs: ["android.hardware.broadcastradio@1.1-utils-lib"],
+}
diff --git a/broadcastradio/1.1/tests/WorkerThread_test.cpp b/broadcastradio/1.1/tests/WorkerThread_test.cpp
new file mode 100644
index 0000000..a0e0ebb
--- /dev/null
+++ b/broadcastradio/1.1/tests/WorkerThread_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <WorkerThread.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+using namespace std::chrono_literals;
+
+using android::WorkerThread;
+
+using std::atomic;
+using std::chrono::time_point;
+using std::chrono::steady_clock;
+using std::is_sorted;
+using std::lock_guard;
+using std::mutex;
+using std::this_thread::sleep_for;
+using std::vector;
+
+#define ASSERT_EQ_WITH_TOLERANCE(val1, val2, tolerance) \
+ ASSERT_LE((val1) - (tolerance), (val2)); \
+ ASSERT_GE((val1) + (tolerance), (val2));
+
+TEST(WorkerThreadTest, oneTask) {
+ atomic<bool> executed(false);
+ atomic<time_point<steady_clock>> stop;
+ WorkerThread thread;
+
+ auto start = steady_clock::now();
+ thread.schedule(
+ [&]() {
+ stop = steady_clock::now();
+ executed = true;
+ },
+ 100ms);
+
+ sleep_for(150ms);
+
+ ASSERT_TRUE(executed);
+ auto delta = stop.load() - start;
+ ASSERT_EQ_WITH_TOLERANCE(delta, 100ms, 50ms);
+}
+
+TEST(WorkerThreadTest, cancelSecond) {
+ atomic<bool> executed1(false);
+ atomic<bool> executed2(false);
+ WorkerThread thread;
+
+ thread.schedule([&]() { executed2 = true; }, 100ms);
+ thread.schedule([&]() { executed1 = true; }, 25ms);
+
+ sleep_for(50ms);
+ thread.cancelAll();
+ sleep_for(100ms);
+
+ ASSERT_TRUE(executed1);
+ ASSERT_FALSE(executed2);
+}
+
+TEST(WorkerThreadTest, executeInOrder) {
+ mutex mut;
+ vector<int> order;
+ WorkerThread thread;
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(0);
+ },
+ 50ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(4);
+ },
+ 400ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(1);
+ },
+ 100ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(3);
+ },
+ 300ms);
+
+ thread.schedule(
+ [&]() {
+ lock_guard<mutex> lk(mut);
+ order.push_back(2);
+ },
+ 200ms);
+
+ sleep_for(500ms);
+
+ ASSERT_EQ(5u, order.size());
+ ASSERT_TRUE(is_sorted(order.begin(), order.end()));
+}
+
+TEST(WorkerThreadTest, dontExecuteAfterDestruction) {
+ atomic<bool> executed1(false);
+ atomic<bool> executed2(false);
+ {
+ WorkerThread thread;
+
+ thread.schedule([&]() { executed2 = true; }, 100ms);
+ thread.schedule([&]() { executed1 = true; }, 25ms);
+
+ sleep_for(50ms);
+ }
+ sleep_for(100ms);
+
+ ASSERT_TRUE(executed1);
+ ASSERT_FALSE(executed2);
+}
+
+} // anonymous namespace
diff --git a/broadcastradio/1.1/types.hal b/broadcastradio/1.1/types.hal
index 5577ea0..dee38b8 100644
--- a/broadcastradio/1.1/types.hal
+++ b/broadcastradio/1.1/types.hal
@@ -68,11 +68,156 @@
};
/**
+ * Type of a radio technology.
+ *
+ * All other values are reserved for future use.
+ */
+enum ProgramType : uint32_t {
+ AM = 1, // analogue AM radio (with or without RDS)
+ FM, // analogue FM radio (with or without RDS)
+ AM_HD, // AM HD Radio
+ FM_HD, // FM HD Radio
+ DAB, // Digital audio broadcasting
+ DRMO, // Digital Radio Mondiale
+ SXM, // SiriusXM Satellite Radio
+ VENDOR, // vendor-specific, not synced across devices
+};
+
+/**
+ * Type of program identifier component.
+ *
+ * It MUST match the radio technology for primary ID but does not have to match
+ * it for secondary IDs. For example, a satellite program may set AM/FM fallback
+ * frequency, if a station broadcasts both via satellite and AM/FM.
+ *
+ * The value format for each (but VENDOR_PRIMARY) identifier is strictly defined
+ * to maintain interoperability between devices made by different vendors.
+ *
+ * All other values are reserved for future use.
+ */
+enum IdentifierType : uint32_t {
+ AMFM_FREQUENCY = 1, // kHz
+ RDS_PI, // 16bit
+
+ /**
+ * 64bit compound primary identifier for HD Radio.
+ *
+ * Consists of (from the LSB):
+ * - 32bit: Station ID number;
+ * - 4bit: HD_SUBCHANNEL;
+ * - 18bit: AMFM_FREQUENCY.
+ * The remaining bits should be set to zeros when writing on the chip side
+ * and ignored when read.
+ */
+ HD_STATION_ID_EXT,
+
+ /**
+ * HD Radio subchannel - a value of range 0-7.
+ *
+ * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
+ * as opposed to HD Radio standard (where it's 1-based).
+ */
+ HD_SUBCHANNEL,
+
+ /**
+ * 24bit compound primary identifier for DAB.
+ *
+ * Consists of (from the LSB):
+ * - 16bit: SId;
+ * - 8bit: ECC code.
+ * The remaining bits should be set to zeros when writing on the chip side
+ * and ignored when read.
+ */
+ DAB_SIDECC,
+
+ DAB_ENSEMBLE, // 16bit
+ DAB_SCID, // 12bit
+ DAB_FREQUENCY, // kHz
+ DRMO_SERVICE_ID, // 24bit
+ DRMO_FREQUENCY, // kHz
+ SXM_SERVICE_ID, // 32bit
+ SXM_CHANNEL, // 0-999 range
+
+ /**
+ * Primary identifier for vendor-specific radio technology.
+ * The value format is determined by a vendor.
+ *
+ * It must not be used in any other programType than VENDOR.
+ */
+ VENDOR_PRIMARY,
+};
+
+/**
+ * A single program identifier component, eg. frequency or channel ID.
+ *
+ * The uint32_t type field maps to IdentifierType enum. It's not straight,
+ * because the enum may be extended in future versions of the HAL. Values out of
+ * the enum range must not be used when writing and ignored when reading.
+ *
+ * The uint64_t value field holds the value in format described in comments for
+ * IdentifierType enum.
+ */
+struct ProgramIdentifier {
+ uint32_t type; // IdentifierType
+ uint64_t value;
+};
+
+/**
+ * A set of identifiers necessary to tune to a given station.
+ *
+ * This can hold various identifiers, like
+ * - AM/FM frequency
+ * - HD Radio subchannel
+ * - DAB channel info
+ *
+ * The uint32_t programType field maps to ProgramType enum. It's not straight,
+ * because the enum may be extended in future versions of the HAL. Values out of
+ * the enum range must not be used when writing and ignored when reading.
+ *
+ * The primary ID uniquely identifies a station and can be used for equality
+ * check. The secondary IDs are supplementary and can speed up tuning process,
+ * but the primary ID is sufficient (ie. after a full band scan).
+ *
+ * Two selectors with different secondary IDs, but the same primary ID are
+ * considered equal. In particular, secondary IDs vector may get updated for
+ * an entry on the program list (ie. when a better frequency for a given
+ * station is found).
+ *
+ * The primaryId of a given programType MUST be of a specific type:
+ * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;
+ * - AM_HD, FM_HD: HD_STATION_ID_EXT;
+ * - DAB: DAB_SIDECC;
+ * - DRMO: DRMO_SERVICE_ID;
+ * - SXM: SXM_SERVICE_ID;
+ * - VENDOR: VENDOR_PRIMARY.
+ */
+struct ProgramSelector {
+ uint32_t programType; // ProgramType
+ ProgramIdentifier primaryId; // uniquely identifies a station
+ vec<ProgramIdentifier> secondaryIds;
+
+ /**
+ * Opaque vendor-specific identifiers, to be passed to front-end
+ * without changes.
+ *
+ * The order is meaningful, ie. the first element may be defined as
+ * frequency, second as the subchannel etc.
+ *
+ * The vector is not serialized (either locally or to the cloud),
+ * unless it's a VENDOR program type.
+ */
+ vec<uint64_t> vendorIds;
+};
+
+/**
* Radio program information. Returned by the HAL with event RADIO_EVENT_TUNED.
* Contains information on currently tuned channel.
*/
struct ProgramInfo {
@1.0::ProgramInfo base;
+
+ ProgramSelector selector;
+
bitfield<ProgramInfoFlags> flags;
/**
diff --git a/broadcastradio/1.1/utils/Android.bp b/broadcastradio/1.1/utils/Android.bp
new file mode 100644
index 0000000..df5e8e0
--- /dev/null
+++ b/broadcastradio/1.1/utils/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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_static {
+ name: "android.hardware.broadcastradio@1.1-utils-lib",
+ vendor: true,
+ relative_install_path: "hw",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+ srcs: [
+ "Utils.cpp",
+ "WorkerThread.cpp",
+ ],
+ export_include_dirs: ["."],
+ shared_libs: [
+ "android.hardware.broadcastradio@1.1",
+ ],
+}
diff --git a/broadcastradio/1.1/utils/Utils.cpp b/broadcastradio/1.1/utils/Utils.cpp
new file mode 100644
index 0000000..9dc0a53
--- /dev/null
+++ b/broadcastradio/1.1/utils/Utils.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "BroadcastRadioDefault.utils"
+//#define LOG_NDEBUG 0
+
+#include "Utils.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+namespace utils {
+
+using V1_0::Band;
+
+static bool isCompatibleProgramType(const uint32_t ia, const uint32_t ib) {
+ auto a = static_cast<ProgramType>(ia);
+ auto b = static_cast<ProgramType>(ib);
+
+ if (a == b) return true;
+ if (a == ProgramType::AM && b == ProgramType::AM_HD) return true;
+ if (a == ProgramType::AM_HD && b == ProgramType::AM) return true;
+ if (a == ProgramType::FM && b == ProgramType::FM_HD) return true;
+ if (a == ProgramType::FM_HD && b == ProgramType::FM) return true;
+ return false;
+}
+
+static bool bothHaveId(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ return hasId(a, type) && hasId(b, type);
+}
+
+static bool anyHaveId(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ return hasId(a, type) || hasId(b, type);
+}
+
+static bool haveEqualIds(const ProgramSelector& a, const ProgramSelector& b,
+ const IdentifierType type) {
+ if (!bothHaveId(a, b, type)) return false;
+ // TODO(b/36864090): we should check all Ids of a given type (ie. other AF), not just one
+ auto aId = getId(a, type);
+ auto bId = getId(b, type);
+ return aId == bId;
+}
+
+bool tunesTo(const ProgramSelector& a, const ProgramSelector& b) {
+ if (!isCompatibleProgramType(a.programType, b.programType)) return false;
+
+ auto type = getType(a);
+
+ switch (type) {
+ case ProgramType::AM:
+ case ProgramType::AM_HD:
+ case ProgramType::FM:
+ case ProgramType::FM_HD:
+ if (haveEqualIds(a, b, IdentifierType::HD_STATION_ID_EXT)) return true;
+
+ // if HD Radio subchannel is specified, it must match
+ if (anyHaveId(a, b, IdentifierType::HD_SUBCHANNEL)) {
+ // missing subchannel (analog) is an equivalent of first subchannel (MPS)
+ auto aCh = getId(a, IdentifierType::HD_SUBCHANNEL, 0);
+ auto bCh = getId(b, IdentifierType::HD_SUBCHANNEL, 0);
+ if (aCh != bCh) return false;
+ }
+
+ if (haveEqualIds(a, b, IdentifierType::RDS_PI)) return true;
+
+ return haveEqualIds(a, b, IdentifierType::AMFM_FREQUENCY);
+ case ProgramType::DAB:
+ return haveEqualIds(a, b, IdentifierType::DAB_SIDECC);
+ case ProgramType::DRMO:
+ return haveEqualIds(a, b, IdentifierType::DRMO_SERVICE_ID);
+ case ProgramType::SXM:
+ if (anyHaveId(a, b, IdentifierType::SXM_SERVICE_ID)) {
+ return haveEqualIds(a, b, IdentifierType::SXM_SERVICE_ID);
+ }
+ return haveEqualIds(a, b, IdentifierType::SXM_CHANNEL);
+ case ProgramType::VENDOR:
+ default:
+ ALOGW("Unsupported program type: %s", toString(type).c_str());
+ return false;
+ }
+}
+
+ProgramType getType(const ProgramSelector& sel) {
+ return static_cast<ProgramType>(sel.programType);
+}
+
+bool isAmFm(const ProgramType type) {
+ switch (type) {
+ case ProgramType::AM:
+ case ProgramType::FM:
+ case ProgramType::AM_HD:
+ case ProgramType::FM_HD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool hasId(const ProgramSelector& sel, const IdentifierType type) {
+ auto itype = static_cast<uint32_t>(type);
+ if (sel.primaryId.type == itype) return true;
+ // not optimal, but we don't care in default impl
+ for (auto&& id : sel.secondaryIds) {
+ if (id.type == itype) return true;
+ }
+ return false;
+}
+
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type) {
+ auto itype = static_cast<uint32_t>(type);
+ if (sel.primaryId.type == itype) return sel.primaryId.value;
+ // not optimal, but we don't care in default impl
+ for (auto&& id : sel.secondaryIds) {
+ if (id.type == itype) return id.value;
+ }
+ ALOGW("Identifier %s not found", toString(type).c_str());
+ return 0;
+}
+
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval) {
+ if (!hasId(sel, type)) return defval;
+ return getId(sel, type);
+}
+
+ProgramSelector make_selector(Band band, uint32_t channel, uint32_t subChannel) {
+ ProgramSelector sel = {};
+
+ ALOGW_IF((subChannel > 0) && (band == Band::AM || band == Band::FM),
+ "got subChannel for non-HD AM/FM");
+
+ // we can't use ProgramType::AM_HD or FM_HD, because we don't know HD station ID
+ ProgramType type;
+ switch (band) {
+ case Band::AM:
+ case Band::AM_HD:
+ type = ProgramType::AM;
+ break;
+ case Band::FM:
+ case Band::FM_HD:
+ type = ProgramType::FM;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unsupported band: %s", toString(band).c_str());
+ }
+
+ sel.programType = static_cast<uint32_t>(type);
+ sel.primaryId.type = static_cast<uint32_t>(IdentifierType::AMFM_FREQUENCY);
+ sel.primaryId.value = channel;
+ if (subChannel > 0) {
+ // stating sub channel for AM/FM channel does not give any guarantees,
+ // but we can't do much more without HD station ID
+ sel.secondaryIds = hidl_vec<ProgramIdentifier>{
+ {static_cast<uint32_t>(IdentifierType::HD_SUBCHANNEL), subChannel},
+ };
+ }
+
+ return sel;
+}
+
+bool getLegacyChannel(const ProgramSelector& sel, uint32_t& channelOut, uint32_t& subChannelOut) {
+ if (isAmFm(getType(sel))) {
+ channelOut = getId(sel, IdentifierType::AMFM_FREQUENCY);
+ subChannelOut = getId(sel, IdentifierType::HD_SUBCHANNEL, 0);
+ return true;
+ } else {
+ channelOut = 0;
+ subChannelOut = 0;
+ return false;
+ }
+}
+
+bool isDigital(const ProgramSelector& sel) {
+ switch (getType(sel)) {
+ case ProgramType::AM:
+ case ProgramType::FM:
+ return false;
+ default:
+ // VENDOR might not be digital, but it doesn't matter for default impl.
+ return true;
+ }
+}
+
+} // namespace utils
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
diff --git a/broadcastradio/1.1/utils/Utils.h b/broadcastradio/1.1/utils/Utils.h
new file mode 100644
index 0000000..1110e79
--- /dev/null
+++ b/broadcastradio/1.1/utils/Utils.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
+
+#include <android/hardware/broadcastradio/1.1/types.h>
+#include <chrono>
+#include <queue>
+#include <thread>
+
+namespace android {
+namespace hardware {
+namespace broadcastradio {
+namespace V1_1 {
+namespace implementation {
+namespace utils {
+
+/**
+ * Checks, if {@code pointer} tunes to {@channel}.
+ *
+ * For example, having a channel {AMFM_FREQUENCY = 103.3}:
+ * - selector {AMFM_FREQUENCY = 103.3, HD_SUBCHANNEL = 0} can tune to this channel;
+ * - selector {AMFM_FREQUENCY = 103.3, HD_SUBCHANNEL = 1} can't.
+ *
+ * @param pointer selector we're trying to match against channel.
+ * @param channel existing channel.
+ */
+bool tunesTo(const ProgramSelector& pointer, const ProgramSelector& channel);
+
+ProgramType getType(const ProgramSelector& sel);
+bool isAmFm(const ProgramType type);
+
+bool hasId(const ProgramSelector& sel, const IdentifierType type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns 0 and emits a warning.
+ */
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type);
+
+/**
+ * Returns ID (either primary or secondary) for a given program selector.
+ *
+ * If the selector does not contain given type, returns default value.
+ */
+uint64_t getId(const ProgramSelector& sel, const IdentifierType type, uint64_t defval);
+
+ProgramSelector make_selector(V1_0::Band band, uint32_t channel, uint32_t subChannel = 0);
+
+bool getLegacyChannel(const ProgramSelector& sel, uint32_t& channelOut, uint32_t& subChannelOut);
+
+bool isDigital(const ProgramSelector& sel);
+
+} // namespace utils
+} // namespace implementation
+} // namespace V1_1
+} // namespace broadcastradio
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_UTILS_H
diff --git a/broadcastradio/1.1/utils/WorkerThread.cpp b/broadcastradio/1.1/utils/WorkerThread.cpp
new file mode 100644
index 0000000..a3ceaa1
--- /dev/null
+++ b/broadcastradio/1.1/utils/WorkerThread.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WorkerThread"
+//#define LOG_NDEBUG 0
+
+#include "WorkerThread.h"
+
+#include <log/log.h>
+
+namespace android {
+
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+using std::function;
+using std::lock_guard;
+using std::mutex;
+using std::priority_queue;
+using std::this_thread::sleep_for;
+using std::unique_lock;
+
+bool operator<(const WorkerThread::Task& lhs, const WorkerThread::Task& rhs) {
+ return lhs.when > rhs.when;
+}
+
+WorkerThread::WorkerThread() : mIsTerminating(false), mThread(&WorkerThread::threadLoop, this) {}
+
+WorkerThread::~WorkerThread() {
+ ALOGV("%s", __func__);
+ {
+ lock_guard<mutex> lk(mMut);
+ mIsTerminating = true;
+ mCond.notify_one();
+ }
+ mThread.join();
+}
+
+void WorkerThread::schedule(function<void()> task, milliseconds delay) {
+ ALOGV("%s", __func__);
+
+ auto when = steady_clock::now() + delay;
+
+ lock_guard<mutex> lk(mMut);
+ mTasks.push(Task({when, task}));
+ mCond.notify_one();
+}
+
+void WorkerThread::cancelAll() {
+ ALOGV("%s", __func__);
+
+ lock_guard<mutex> lk(mMut);
+ priority_queue<Task>().swap(mTasks); // empty queue
+}
+
+void WorkerThread::threadLoop() {
+ ALOGV("%s", __func__);
+ while (!mIsTerminating) {
+ unique_lock<mutex> lk(mMut);
+ if (mTasks.empty()) {
+ mCond.wait(lk);
+ continue;
+ }
+
+ auto task = mTasks.top();
+ if (task.when > steady_clock::now()) {
+ mCond.wait_until(lk, task.when);
+ continue;
+ }
+
+ mTasks.pop();
+ lk.unlock(); // what() might need to schedule another task
+ task.what();
+ }
+}
+
+} // namespace android
diff --git a/broadcastradio/1.1/utils/WorkerThread.h b/broadcastradio/1.1/utils/WorkerThread.h
new file mode 100644
index 0000000..635876f
--- /dev/null
+++ b/broadcastradio/1.1/utils/WorkerThread.h
@@ -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.
+ */
+#ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
+#define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
+
+#include <chrono>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+class WorkerThread {
+ public:
+ WorkerThread();
+ virtual ~WorkerThread();
+
+ void schedule(std::function<void()> task, std::chrono::milliseconds delay);
+ void cancelAll();
+
+ private:
+ struct Task {
+ std::chrono::time_point<std::chrono::steady_clock> when;
+ std::function<void()> what;
+ };
+ friend bool operator<(const Task& lhs, const Task& rhs);
+
+ std::atomic<bool> mIsTerminating;
+ std::mutex mMut;
+ std::condition_variable mCond;
+ std::thread mThread;
+ std::priority_queue<Task> mTasks;
+
+ void threadLoop();
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_WORKERTHREAD_H
diff --git a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
index aa5ab54..0de6fa5 100644
--- a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
+++ b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
@@ -48,7 +48,7 @@
using ::android::hardware::broadcastradio::V1_1::ProgramInfo;
using ::android::hardware::broadcastradio::V1_1::Result;
using ::android::hardware::broadcastradio::V1_1::ProgramListResult;
-
+using ::android::hardware::hidl_vec;
// The main test class for Broadcast Radio HIDL HAL.
@@ -56,14 +56,15 @@
protected:
virtual void SetUp() override {
auto factory = ::testing::VtsHalHidlTargetTestBase::getService<IBroadcastRadioFactory>();
- if (factory != 0) {
- factory->connectModule(Class::AM_FM,
- [&](Result retval, const ::android::sp<IBroadcastRadio>& result) {
- if (retval == Result::OK) {
- mRadio = IBroadcastRadio::castFrom(result);
- }
- });
- }
+ ASSERT_NE(nullptr, factory.get());
+ Result halResult;
+ factory->connectModule(Class::AM_FM, [&](Result retval, const sp<IBroadcastRadio>& result) {
+ halResult = retval;
+ if (retval == Result::OK) {
+ mRadio = IBroadcastRadio::castFrom(result);
+ }
+ });
+ ASSERT_EQ(Result::OK, halResult);
mTunerCallback = new MyCallback(this);
ASSERT_NE(nullptr, mRadio.get());
ASSERT_NE(nullptr, mTunerCallback.get());
@@ -133,7 +134,9 @@
return Void();
}
- virtual Return<void> backgroundScanComplete(ProgramListResult result __unused) {
+ virtual Return<void> backgroundScanComplete(ProgramListResult result) {
+ ALOGV("%s", __func__);
+ mParentTest->onProgramListResultCallback(result);
return Void();
}
@@ -176,6 +179,15 @@
}
/**
+ * Method called by MyCallback when a callback with status is received
+ */
+ void onProgramListResultCallback(ProgramListResult result) {
+ Mutex::Autolock _l(mLock);
+ mProgramListResultCallbackData = result;
+ onCallback_l();
+ }
+
+ /**
* Method called by MyCallback when a boolean indication is received
*/
void onBoolCallback(bool result) {
@@ -220,6 +232,7 @@
static const nsecs_t kConfigCallbacktimeoutNs = seconds_to_nanoseconds(10);
static const nsecs_t kTuneCallbacktimeoutNs = seconds_to_nanoseconds(30);
+ static const nsecs_t kFullScanTimeoutNs = seconds_to_nanoseconds(60);
sp<IBroadcastRadio> mRadio;
Properties mHalProperties;
@@ -230,6 +243,7 @@
bool mCallbackCalled;
bool mBoolCallbackData;
Result mResultCallbackData;
+ ProgramListResult mProgramListResultCallbackData;
bool mHwFailure;
};
@@ -277,6 +291,7 @@
});
EXPECT_TRUE(hidlReturn.isOk());
EXPECT_EQ(Result::OK, halResult);
+ EXPECT_NE(nullptr, mTuner.get());
EXPECT_TRUE(waitForCallback(kConfigCallbacktimeoutNs));
}
EXPECT_NE(nullptr, mTuner.get());
@@ -454,11 +469,8 @@
EXPECT_EQ(Result::OK, halResult);
auto &halInfo_1_1 = halInfo.base;
if (mResultCallbackData == Result::OK) {
- EXPECT_TRUE(halInfo_1_1.tuned);
EXPECT_LE(halInfo_1_1.channel, upperLimit);
EXPECT_GE(halInfo_1_1.channel, lowerLimit);
- } else {
- EXPECT_EQ(false, halInfo_1_1.tuned);
}
// test cancel
@@ -468,6 +480,53 @@
EXPECT_EQ(Result::OK, hidlResult);
}
+TEST_F(BroadcastRadioHidlTest, TuneFromProgramList) {
+ ASSERT_TRUE(openTuner());
+ ASSERT_TRUE(checkAntenna());
+
+ ProgramInfo firstProgram;
+ bool isListEmpty;
+ ProgramListResult getListResult = ProgramListResult::NOT_INITIALIZED;
+ auto getListCb = [&](ProgramListResult result, const hidl_vec<ProgramInfo>& list) {
+ getListResult = result;
+ if (result != ProgramListResult::OK) return;
+ isListEmpty = (list.size() == 0);
+ // don't copy the whole list out, it might be heavy
+ if (!isListEmpty) firstProgram = list[0];
+ };
+
+ // first try...
+ auto hidlReturn = mTuner->getProgramList("", getListCb);
+ ASSERT_TRUE(hidlReturn.isOk());
+
+ if (getListResult == ProgramListResult::NOT_STARTED) {
+ auto result = mTuner->startBackgroundScan();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(ProgramListResult::OK, result);
+ getListResult = ProgramListResult::NOT_READY; // continue as in NOT_READY case
+ }
+ if (getListResult == ProgramListResult::NOT_READY) {
+ ASSERT_TRUE(waitForCallback(kFullScanTimeoutNs));
+ ASSERT_EQ(ProgramListResult::OK, mProgramListResultCallbackData);
+
+ // second (last) try...
+ hidlReturn = mTuner->getProgramList("", getListCb);
+ ASSERT_TRUE(hidlReturn.isOk());
+ ASSERT_EQ(ProgramListResult::OK, getListResult);
+ }
+
+ if (isListEmpty) {
+ std::cout << "[ SKIPPED ] Program list is empty. " << std::endl;
+ return;
+ }
+
+ auto tuneResult = mTuner->tune_1_1(firstProgram.selector);
+ ASSERT_TRUE(tuneResult.isOk());
+ EXPECT_EQ(Result::OK, tuneResult);
+ EXPECT_EQ(true, waitForCallback(kTuneCallbacktimeoutNs));
+ // TODO(b/36864490): check this too, when mProgramInfoCallbackData is cherry-picked from 1.0
+ // EXPECT_EQ(firstProgram.selector.primaryId, mProgramInfoCallbackData.selector.primaryId);
+}
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(new BroadcastRadioHidlEnvironment);
diff --git a/broadcastradio/Android.bp b/broadcastradio/Android.bp
index 7a315fa..a5ad5e7 100644
--- a/broadcastradio/Android.bp
+++ b/broadcastradio/Android.bp
@@ -5,5 +5,7 @@
"1.0/vts/functional",
"1.1",
"1.1/default",
+ "1.1/tests",
+ "1.1/utils",
"1.1/vts/functional",
]
diff --git a/camera/common/1.0/default/CameraParameters.cpp b/camera/common/1.0/default/CameraParameters.cpp
index d224483..e707b08 100644
--- a/camera/common/1.0/default/CameraParameters.cpp
+++ b/camera/common/1.0/default/CameraParameters.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
#include "CameraParameters.h"
#include <system/graphics.h>
diff --git a/cas/1.0/Android.bp b/cas/1.0/Android.bp
new file mode 100644
index 0000000..2251f11
--- /dev/null
+++ b/cas/1.0/Android.bp
@@ -0,0 +1,84 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+filegroup {
+ name: "android.hardware.cas@1.0_hal",
+ srcs: [
+ "types.hal",
+ "ICas.hal",
+ "ICasListener.hal",
+ "IDescramblerBase.hal",
+ "IMediaCasService.hal",
+ ],
+}
+
+genrule {
+ name: "android.hardware.cas@1.0_genc++",
+ tools: ["hidl-gen"],
+ cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.cas@1.0",
+ srcs: [
+ ":android.hardware.cas@1.0_hal",
+ ],
+ out: [
+ "android/hardware/cas/1.0/types.cpp",
+ "android/hardware/cas/1.0/CasAll.cpp",
+ "android/hardware/cas/1.0/CasListenerAll.cpp",
+ "android/hardware/cas/1.0/DescramblerBaseAll.cpp",
+ "android/hardware/cas/1.0/MediaCasServiceAll.cpp",
+ ],
+}
+
+genrule {
+ name: "android.hardware.cas@1.0_genc++_headers",
+ tools: ["hidl-gen"],
+ cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.cas@1.0",
+ srcs: [
+ ":android.hardware.cas@1.0_hal",
+ ],
+ out: [
+ "android/hardware/cas/1.0/types.h",
+ "android/hardware/cas/1.0/hwtypes.h",
+ "android/hardware/cas/1.0/ICas.h",
+ "android/hardware/cas/1.0/IHwCas.h",
+ "android/hardware/cas/1.0/BnHwCas.h",
+ "android/hardware/cas/1.0/BpHwCas.h",
+ "android/hardware/cas/1.0/BsCas.h",
+ "android/hardware/cas/1.0/ICasListener.h",
+ "android/hardware/cas/1.0/IHwCasListener.h",
+ "android/hardware/cas/1.0/BnHwCasListener.h",
+ "android/hardware/cas/1.0/BpHwCasListener.h",
+ "android/hardware/cas/1.0/BsCasListener.h",
+ "android/hardware/cas/1.0/IDescramblerBase.h",
+ "android/hardware/cas/1.0/IHwDescramblerBase.h",
+ "android/hardware/cas/1.0/BnHwDescramblerBase.h",
+ "android/hardware/cas/1.0/BpHwDescramblerBase.h",
+ "android/hardware/cas/1.0/BsDescramblerBase.h",
+ "android/hardware/cas/1.0/IMediaCasService.h",
+ "android/hardware/cas/1.0/IHwMediaCasService.h",
+ "android/hardware/cas/1.0/BnHwMediaCasService.h",
+ "android/hardware/cas/1.0/BpHwMediaCasService.h",
+ "android/hardware/cas/1.0/BsMediaCasService.h",
+ ],
+}
+
+cc_library_shared {
+ name: "android.hardware.cas@1.0",
+ defaults: ["hidl-module-defaults"],
+ generated_sources: ["android.hardware.cas@1.0_genc++"],
+ generated_headers: ["android.hardware.cas@1.0_genc++_headers"],
+ export_generated_headers: ["android.hardware.cas@1.0_genc++_headers"],
+ vendor_available: true,
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "libcutils",
+ ],
+ export_shared_lib_headers: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ ],
+}
diff --git a/cas/1.0/CasHal.mk b/cas/1.0/CasHal.mk
new file mode 100644
index 0000000..3cae6bf
--- /dev/null
+++ b/cas/1.0/CasHal.mk
@@ -0,0 +1,192 @@
+#
+# 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.
+
+
+########################################################################
+# Included by frameworks/base for MediaCas. Hidl HAL can't be linked as
+# Java lib from frameworks because it has dependency on frameworks itself.
+#
+
+intermediates := $(TARGET_OUT_COMMON_GEN)/JAVA_LIBRARIES/android.hardware.cas-V1.0-java_intermediates
+
+HIDL := $(HOST_OUT_EXECUTABLES)/hidl-gen$(HOST_EXECUTABLE_SUFFIX)
+HIDL_PATH := system/libhidl/transport/base/1.0
+
+#
+# Build types.hal (DebugInfo)
+#
+GEN := $(intermediates)/android/hidl/base/V1_0/DebugInfo.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hidl:system/libhidl/transport \
+ android.hidl.base@1.0::types.DebugInfo
+
+$(GEN): $(HIDL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IBase.hal
+#
+GEN := $(intermediates)/android/hidl/base/V1_0/IBase.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/IBase.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/types.hal
+$(GEN): $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hidl:system/libhidl/transport \
+ android.hidl.base@1.0::IBase
+
+$(GEN): $(HIDL_PATH)/IBase.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+HIDL_PATH := hardware/interfaces/cas/1.0
+
+#
+# Build types.hal (HidlCasPluginDescriptor)
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/HidlCasPluginDescriptor.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::types.HidlCasPluginDescriptor
+
+$(GEN): $(HIDL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build types.hal (Status)
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/Status.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::types.Status
+
+$(GEN): $(HIDL_PATH)/types.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build ICas.hal
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/ICas.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/ICas.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/types.hal
+$(GEN): $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::ICas
+
+$(GEN): $(HIDL_PATH)/ICas.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build ICasListener.hal
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/ICasListener.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/ICasListener.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::ICasListener
+
+$(GEN): $(HIDL_PATH)/ICasListener.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IDescramblerBase.hal
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/IDescramblerBase.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/IDescramblerBase.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/types.hal
+$(GEN): $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::IDescramblerBase
+
+$(GEN): $(HIDL_PATH)/IDescramblerBase.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+#
+# Build IMediaCasService.hal
+#
+GEN := $(intermediates)/android/hardware/cas/V1_0/IMediaCasService.java
+$(GEN): $(HIDL)
+$(GEN): PRIVATE_HIDL := $(HIDL)
+$(GEN): PRIVATE_DEPS := $(HIDL_PATH)/IMediaCasService.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/ICas.hal
+$(GEN): $(HIDL_PATH)/ICas.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/ICasListener.hal
+$(GEN): $(HIDL_PATH)/ICasListener.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/IDescramblerBase.hal
+$(GEN): $(HIDL_PATH)/IDescramblerBase.hal
+$(GEN): PRIVATE_DEPS += $(HIDL_PATH)/types.hal
+$(GEN): $(HIDL_PATH)/types.hal
+$(GEN): PRIVATE_OUTPUT_DIR := $(intermediates)
+$(GEN): PRIVATE_CUSTOM_TOOL = \
+ $(PRIVATE_HIDL) -o $(PRIVATE_OUTPUT_DIR) \
+ -Ljava \
+ -randroid.hardware:hardware/interfaces \
+ -randroid.hidl:system/libhidl/transport \
+ android.hardware.cas@1.0::IMediaCasService
+
+$(GEN): $(HIDL_PATH)/IMediaCasService.hal
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
diff --git a/cas/1.0/ICas.hal b/cas/1.0/ICas.hal
new file mode 100644
index 0000000..08a92da
--- /dev/null
+++ b/cas/1.0/ICas.hal
@@ -0,0 +1,122 @@
+/*
+ * 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.cas@1.0;
+
+import android.hardware.cas@1.0::types;
+
+/**
+ * ICas is the API to control the cas system and is accessible from both
+ * Java and native level. It is used to manage sessions, provision/refresh
+ * the cas system, and process the EMM/ECM messages. It also allows bi-directional,
+ * scheme-specific communications between the client and the cas system.
+ */
+
+interface ICas {
+ /**
+ * Provide the CA private data from a CA_descriptor in the conditional
+ * access table to a CasPlugin.
+ *
+ * @param pvtData a byte array containing the private data, the format of
+ * which is scheme-specific and opaque to the framework.
+ * @return status the status of the call.
+ */
+ setPrivateData(vec<uint8_t> pvtData) generates (Status status);
+
+ /**
+ * Open a session to descramble one or more streams scrambled by the
+ * conditional access system.
+ *
+ * @return status the status of the call.
+ * @return sessionId the id of the newly opened session.
+ */
+ openSession() generates(Status status, HidlCasSessionId sessionId);
+
+ /**
+ * Close a session.
+ *
+ * @param sessionId the id of the session to be closed.
+ * @return status the status of the call.
+ */
+ closeSession(HidlCasSessionId sessionId) generates (Status status);
+
+ /**
+ * Provide the CA private data from a CA_descriptor in the program map
+ * table to a session.
+ *
+ * @param sessionId the id of the session which the private data applies to.
+ * @param pvtData a byte array containing the private data, the format of
+ * which is scheme-specific and opaque to the framework.
+ * @return status the status of the call.
+ */
+ setSessionPrivateData(HidlCasSessionId sessionId, vec<uint8_t> pvtData)
+ generates (Status status);
+
+ /**
+ * Process an ECM from the ECM stream for this session’s elementary stream.
+ *
+ * @param sessionId the id of the session which the ecm data applies to.
+ * @param ecm a byte array containing the ecm data.
+ * @return status the status of the call.
+ */
+ processEcm(HidlCasSessionId sessionId, vec<uint8_t> ecm)
+ generates (Status status);
+
+ /**
+ * Process an in-band EMM from the EMM stream.
+ *
+ * @param emm a byte array containing the emm data.
+ * @return status the status of the call.
+ */
+ processEmm(vec<uint8_t> emm) generates (Status status);
+
+ /**
+ * Send an scheme-specific event to the CasPlugin.
+ *
+ * @param event an integer denoting a scheme-specific event to be sent.
+ * @param arg a scheme-specific integer argument for the event.
+ * @param data a byte array containing scheme-specific data for the event.
+ * @return status the status of the call.
+ */
+ sendEvent(int32_t event, int32_t arg, vec<uint8_t> eventData)
+ generates (Status status);
+
+ /**
+ * Initiate a provisioning operation for a CA system.
+ *
+ * @param provisionString string containing information needed for the
+ * provisioning operation, the format of which is scheme and implementation
+ * specific.
+ * @return status the status of the call.
+ */
+ provision(string provisionString) generates (Status status);
+
+ /**
+ * Notify the CA system to refresh entitlement keys.
+ *
+ * @param refreshType the type of the refreshment.
+ * @param refreshData private data associated with the refreshment.
+ * @return status the status of the call.
+ */
+ refreshEntitlements(int32_t refreshType, vec<uint8_t> refreshData)
+ generates (Status status);
+
+ /**
+ * Release the descrambler instance.
+ *
+ * @return status the status of the call.
+ */
+ release() generates (Status status);
+};
diff --git a/cas/1.0/ICasListener.hal b/cas/1.0/ICasListener.hal
new file mode 100644
index 0000000..8ae6014
--- /dev/null
+++ b/cas/1.0/ICasListener.hal
@@ -0,0 +1,28 @@
+/*
+ * 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.cas@1.0;
+
+interface ICasListener {
+ /**
+ * Notify the listener of a scheme-specific event from the CA system.
+ *
+ * @param event an integer whose meaning is scheme-specific.
+ * @param arg an integer whose meaning is scheme-specific.
+ * @param data a byte array of data whose format and meaning are
+ * scheme-specific.
+ */
+ onEvent(int32_t event, int32_t arg, vec<uint8_t> data);
+};
diff --git a/cas/1.0/IDescramblerBase.hal b/cas/1.0/IDescramblerBase.hal
new file mode 100644
index 0000000..a126084
--- /dev/null
+++ b/cas/1.0/IDescramblerBase.hal
@@ -0,0 +1,49 @@
+/*
+ * 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.cas@1.0;
+
+import android.hardware.cas@1.0::types;
+
+/**
+ * IDescramblerBase is the API to control the descrambler and is accessible
+ * from both Java and native level.
+ */
+
+interface IDescramblerBase {
+ /**
+ * Associate a MediaCas session with this MediaDescrambler instance.
+ *
+ * @param sessionId the id of the session to associate with this descrambler instance.
+ * @return status the status of the call.
+ */
+ setMediaCasSession(HidlCasSessionId sessionId) generates (Status status);
+
+ /**
+ * Query if the scrambling scheme requires the use of a secure decoder
+ * to decode data of the given mime type.
+ *
+ * @param mime the mime type of the media data.
+ * @return result whether the descrambler requires a secure decoder.
+ */
+ requiresSecureDecoderComponent(string mime) generates (bool result);
+
+ /**
+ * Release the descrambler instance.
+ *
+ * @return status the status of the call.
+ */
+ release() generates (Status status);
+};
diff --git a/cas/1.0/IMediaCasService.hal b/cas/1.0/IMediaCasService.hal
new file mode 100644
index 0000000..cfeafad
--- /dev/null
+++ b/cas/1.0/IMediaCasService.hal
@@ -0,0 +1,68 @@
+/*
+ * 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.cas@1.0;
+
+import android.hardware.cas@1.0::ICas;
+import android.hardware.cas@1.0::ICasListener;
+import android.hardware.cas@1.0::IDescramblerBase;
+
+/**
+ * IMediaCasService is the main entry point for interacting with a vendor's
+ * cas HAL to create cas and descrambler plugin instances. A cas plugin instance
+ * opens cas sessions which are used to obtain keys for a descrambler session,
+ * which can in turn be used to descramble protected video content.
+ */
+interface IMediaCasService {
+ /**
+ * List all available CA systems on the device.
+ *
+ * @return descriptors an array of descriptors for the available CA systems.
+ */
+ enumeratePlugins() generates (vec<HidlCasPluginDescriptor> descriptors);
+
+ /**
+ * Query if a certain CA system is supported on this device.
+ *
+ * @param CA_system_id the id of the CA system.
+ * @return result whether the specified CA system is supported on this device.
+ */
+ isSystemIdSupported(int32_t CA_system_id) generates (bool result);
+
+ /**
+ * Construct a new instance of a CasPlugin given a CA_system_id.
+ *
+ * @param CA_system_id the id of the CA system.
+ * @param listener the event listener to receive events coming from the CasPlugin.
+ * @return cas the newly created CasPlugin interface.
+ */
+ createPlugin(int32_t CA_system_id, ICasListener listener) generates (ICas cas);
+
+ /**
+ * Query if the descrambling scheme for a CA system is supported on this device.
+ *
+ * @param CA_system_id the id of the CA system.
+ * @return result whether the specified descrambling scheme is supported on this device.
+ */
+ isDescramblerSupported(int32_t CA_system_id) generates (bool result);
+
+ /**
+ * Construct a new instance of a DescramblerPlugin given a CA_system_id.
+ *
+ * @param CA_system_id the id of the CA system.
+ * @return descrambler the newly created plugin interface.
+ */
+ createDescrambler(int32_t CA_system_id) generates (IDescramblerBase descrambler);
+};
diff --git a/cas/1.0/default/Android.bp b/cas/1.0/default/Android.bp
new file mode 100644
index 0000000..953aa37
--- /dev/null
+++ b/cas/1.0/default/Android.bp
@@ -0,0 +1,35 @@
+cc_binary {
+ name: "android.hardware.cas@1.0-service",
+ defaults: ["hidl_defaults"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "CasImpl.cpp",
+ "DescramblerImpl.cpp",
+ "MediaCasService.cpp",
+ "service.cpp",
+ "SharedLibrary.cpp",
+ "TypeConvert.cpp",
+ ],
+
+ product_variables: {
+ treble: {
+ cflags: ["-DUSE_VNDBINDER"],
+ },
+ },
+
+ compile_multilib: "32",
+ init_rc: ["android.hardware.cas@1.0-service.rc"],
+
+ shared_libs: [
+ "android.hardware.cas@1.0",
+ "android.hardware.cas.native@1.0",
+ "android.hidl.memory@1.0",
+ "libbinder",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/cas/1.0/default/CasImpl.cpp b/cas/1.0/default/CasImpl.cpp
new file mode 100644
index 0000000..9d1f4a3
--- /dev/null
+++ b/cas/1.0/default/CasImpl.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-CasImpl"
+
+#include <android/hardware/cas/1.0/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+struct CasImpl::PluginHolder : public RefBase {
+public:
+ explicit PluginHolder(CasPlugin *plugin) : mPlugin(plugin) {}
+ ~PluginHolder() { if (mPlugin != NULL) delete mPlugin; }
+ CasPlugin* get() { return mPlugin; }
+
+private:
+ CasPlugin *mPlugin;
+ DISALLOW_EVIL_CONSTRUCTORS(PluginHolder);
+};
+
+CasImpl::CasImpl(const sp<ICasListener> &listener)
+ : mPluginHolder(NULL), mListener(listener) {
+ ALOGV("CTOR");
+}
+
+CasImpl::~CasImpl() {
+ ALOGV("DTOR");
+ release();
+}
+
+//static
+void CasImpl::OnEvent(
+ void *appData,
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size) {
+ if (appData == NULL) {
+ ALOGE("Invalid appData!");
+ return;
+ }
+ CasImpl *casImpl = static_cast<CasImpl *>(appData);
+ casImpl->onEvent(event, arg, data, size);
+}
+
+void CasImpl::init(const sp<SharedLibrary>& library, CasPlugin *plugin) {
+ mLibrary = library;
+ mPluginHolder = new PluginHolder(plugin);
+}
+
+void CasImpl::onEvent(
+ int32_t event, int32_t arg, uint8_t *data, size_t size) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ HidlCasData eventData;
+ if (data != NULL) {
+ eventData.setToExternal(data, size);
+ }
+
+ mListener->onEvent(event, arg, eventData);
+}
+
+Return<Status> CasImpl::setPrivateData(const HidlCasData& pvtData) {
+ ALOGV("%s", __FUNCTION__);
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->get()->setPrivateData(pvtData));
+}
+
+Return<void> CasImpl::openSession(openSession_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+ CasSessionId sessionId;
+
+ sp<PluginHolder> holder = mPluginHolder;
+ status_t err = INVALID_OPERATION;
+ if (holder != NULL) {
+ err = holder->get()->openSession(&sessionId);
+ }
+
+ _hidl_cb(toStatus(err), sessionId);
+
+ return Void();
+}
+
+Return<Status> CasImpl::setSessionPrivateData(
+ const HidlCasSessionId &sessionId, const HidlCasData& pvtData) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__,
+ sessionIdToString(sessionId).string());
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(
+ holder->get()->setSessionPrivateData(
+ sessionId, pvtData));
+}
+
+Return<Status> CasImpl::closeSession(const HidlCasSessionId &sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__,
+ sessionIdToString(sessionId).string());
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+ return toStatus(holder->get()->closeSession(sessionId));
+}
+
+Return<Status> CasImpl::processEcm(
+ const HidlCasSessionId &sessionId, const HidlCasData& ecm) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__,
+ sessionIdToString(sessionId).string());
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->get()->processEcm(sessionId, ecm));
+}
+
+Return<Status> CasImpl::processEmm(const HidlCasData& emm) {
+ ALOGV("%s", __FUNCTION__);
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->get()->processEmm(emm));
+}
+
+Return<Status> CasImpl::sendEvent(
+ int32_t event, int32_t arg,
+ const HidlCasData& eventData) {
+ ALOGV("%s", __FUNCTION__);
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->get()->sendEvent(event, arg, eventData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::provision(const hidl_string& provisionString) {
+ ALOGV("%s: provisionString=%s", __FUNCTION__, provisionString.c_str());
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ return toStatus(holder->get()->provision(String8(provisionString.c_str())));
+}
+
+Return<Status> CasImpl::refreshEntitlements(
+ int32_t refreshType,
+ const HidlCasData& refreshData) {
+ ALOGV("%s", __FUNCTION__);
+ sp<PluginHolder> holder = mPluginHolder;
+ if (holder == NULL) {
+ return toStatus(INVALID_OPERATION);
+ }
+
+ status_t err = holder->get()->refreshEntitlements(refreshType, refreshData);
+ return toStatus(err);
+}
+
+Return<Status> CasImpl::release() {
+ ALOGV("%s: plugin=%p", __FUNCTION__,
+ mPluginHolder != NULL ? mPluginHolder->get() : NULL);
+ mPluginHolder.clear();
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.0/default/CasImpl.h b/cas/1.0/default/CasImpl.h
new file mode 100644
index 0000000..841d64e
--- /dev/null
+++ b/cas/1.0/default/CasImpl.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_CAS_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_0_CAS_IMPL_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <android/hardware/cas/1.0/ICas.h>
+
+namespace android {
+struct CasPlugin;
+
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+struct ICasListener;
+namespace implementation {
+
+class SharedLibrary;
+
+class CasImpl : public ICas {
+public:
+ CasImpl(const sp<ICasListener> &listener);
+ virtual ~CasImpl();
+
+ static void OnEvent(
+ void *appData,
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size);
+
+ void init(const sp<SharedLibrary>& library, CasPlugin *plugin);
+ void onEvent(
+ int32_t event,
+ int32_t arg,
+ uint8_t *data,
+ size_t size);
+
+ // ICas inherits
+
+ virtual Return<Status> setPrivateData(
+ const HidlCasData& pvtData) override;
+
+ virtual Return<void> openSession(
+ openSession_cb _hidl_cb) override;
+
+ virtual Return<Status> closeSession(
+ const HidlCasSessionId& sessionId) override;
+
+ virtual Return<Status> setSessionPrivateData(
+ const HidlCasSessionId& sessionId,
+ const HidlCasData& pvtData) override;
+
+ virtual Return<Status> processEcm(
+ const HidlCasSessionId& sessionId,
+ const HidlCasData& ecm) override;
+
+ virtual Return<Status> processEmm(
+ const HidlCasData& emm) override;
+
+ virtual Return<Status> sendEvent(
+ int32_t event, int32_t arg,
+ const HidlCasData& eventData) override;
+
+ virtual Return<Status> provision(
+ const hidl_string& provisionString) override;
+
+ virtual Return<Status> refreshEntitlements(
+ int32_t refreshType,
+ const HidlCasData& refreshData) override;
+
+ virtual Return<Status> release() override;
+
+private:
+ struct PluginHolder;
+ sp<SharedLibrary> mLibrary;
+ sp<PluginHolder> mPluginHolder;
+ sp<ICasListener> mListener;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CasImpl);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_CAS_IMPL_H_
diff --git a/cas/1.0/default/DescramblerImpl.cpp b/cas/1.0/default/DescramblerImpl.cpp
new file mode 100644
index 0000000..3d90809
--- /dev/null
+++ b/cas/1.0/default/DescramblerImpl.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-DescramblerImpl"
+
+#include <hidlmemory/mapping.h>
+#include <media/hardware/CryptoAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "DescramblerImpl.h"
+#include "SharedLibrary.h"
+#include "TypeConvert.h"
+
+namespace android {
+using hidl::memory::V1_0::IMemory;
+
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+#define CHECK_SUBSAMPLE_DEF(type) \
+static_assert(sizeof(SubSample) == sizeof(type::SubSample), \
+ "SubSample: size doesn't match"); \
+static_assert(offsetof(SubSample, numBytesOfClearData) \
+ == offsetof(type::SubSample, mNumBytesOfClearData), \
+ "SubSample: numBytesOfClearData offset doesn't match"); \
+static_assert(offsetof(SubSample, numBytesOfEncryptedData) \
+ == offsetof(type::SubSample, mNumBytesOfEncryptedData), \
+ "SubSample: numBytesOfEncryptedData offset doesn't match")
+
+CHECK_SUBSAMPLE_DEF(DescramblerPlugin);
+CHECK_SUBSAMPLE_DEF(CryptoPlugin);
+
+DescramblerImpl::DescramblerImpl(
+ const sp<SharedLibrary>& library, DescramblerPlugin *plugin) :
+ mLibrary(library), mPlugin(plugin) {
+ ALOGV("CTOR: mPlugin=%p", mPlugin);
+}
+
+DescramblerImpl::~DescramblerImpl() {
+ ALOGV("DTOR: mPlugin=%p", mPlugin);
+ release();
+}
+
+Return<Status> DescramblerImpl::setMediaCasSession(const HidlCasSessionId& sessionId) {
+ ALOGV("%s: sessionId=%s", __FUNCTION__,
+ sessionIdToString(sessionId).string());
+
+ return toStatus(mPlugin->setMediaCasSession(sessionId));
+}
+
+Return<bool> DescramblerImpl::requiresSecureDecoderComponent(
+ const hidl_string& mime) {
+ return mPlugin->requiresSecureDecoderComponent(String8(mime.c_str()));
+}
+
+Return<void> DescramblerImpl::descramble(
+ ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer,
+ uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer,
+ uint64_t dstOffset,
+ descramble_cb _hidl_cb) {
+ ALOGV("%s", __FUNCTION__);
+
+ sp<IMemory> srcMem = mapMemory(srcBuffer.heapBase);
+ void *srcPtr = (uint8_t *)(void *)srcMem->getPointer() + srcBuffer.offset;
+ void *dstPtr = NULL;
+ if (dstBuffer.type == BufferType::SHARED_MEMORY) {
+ // When using shared memory, src buffer is also used as dst,
+ // we don't map it again here.
+ dstPtr = srcPtr;
+ } else {
+ native_handle_t *handle = const_cast<native_handle_t *>(
+ dstBuffer.secureMemory.getNativeHandle());
+ dstPtr = static_cast<void *>(handle);
+ }
+ // Casting hidl SubSample to DescramblerPlugin::SubSample, but need
+ // to ensure structs are actually idential
+
+ int32_t result = mPlugin->descramble(
+ dstBuffer.type != BufferType::SHARED_MEMORY,
+ (DescramblerPlugin::ScramblingControl)scramblingControl,
+ subSamples.size(),
+ (DescramblerPlugin::SubSample*)subSamples.data(),
+ srcPtr,
+ srcOffset,
+ dstPtr,
+ dstOffset,
+ NULL);
+
+ _hidl_cb(toStatus(result >= 0 ? OK : result), result, NULL);
+ return Void();
+}
+
+Return<Status> DescramblerImpl::release() {
+ ALOGV("%s: mPlugin=%p", __FUNCTION__, mPlugin);
+
+ if (mPlugin != NULL) {
+ delete mPlugin;
+ mPlugin = NULL;
+ }
+ return Status::OK;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.0/default/DescramblerImpl.h b/cas/1.0/default/DescramblerImpl.h
new file mode 100644
index 0000000..d3b146e
--- /dev/null
+++ b/cas/1.0/default/DescramblerImpl.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_DESCRAMBLER_IMPL_H_
+#define ANDROID_HARDWARE_CAS_V1_0_DESCRAMBLER_IMPL_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+
+namespace android {
+struct DescramblerPlugin;
+using namespace hardware::cas::native::V1_0;
+
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+class SharedLibrary;
+
+class DescramblerImpl : public IDescrambler {
+public:
+ DescramblerImpl(const sp<SharedLibrary>& library, DescramblerPlugin *plugin);
+ virtual ~DescramblerImpl();
+
+ virtual Return<Status> setMediaCasSession(
+ const HidlCasSessionId& sessionId) override;
+
+ virtual Return<bool> requiresSecureDecoderComponent(
+ const hidl_string& mime) override;
+
+ virtual Return<void> descramble(
+ ScramblingControl scramblingControl,
+ const hidl_vec<SubSample>& subSamples,
+ const SharedBuffer& srcBuffer,
+ uint64_t srcOffset,
+ const DestinationBuffer& dstBuffer,
+ uint64_t dstOffset,
+ descramble_cb _hidl_cb) override;
+
+ virtual Return<Status> release() override;
+
+private:
+ sp<SharedLibrary> mLibrary;
+ DescramblerPlugin *mPlugin;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DescramblerImpl);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_DESCRAMBLER_IMPL_H_
diff --git a/cas/1.0/default/FactoryLoader.h b/cas/1.0/default/FactoryLoader.h
new file mode 100644
index 0000000..18c2186
--- /dev/null
+++ b/cas/1.0/default/FactoryLoader.h
@@ -0,0 +1,229 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_FACTORY_LOADER_H_
+#define ANDROID_HARDWARE_CAS_V1_0_FACTORY_LOADER_H_
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include "SharedLibrary.h"
+#include <utils/KeyedVector.h>
+#include <utils/Mutex.h>
+#include <media/cas/CasAPI.h>
+
+using namespace std;
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+template <class T>
+class FactoryLoader {
+public:
+ FactoryLoader(const char *name) :
+ mFactory(NULL), mCreateFactoryFuncName(name) {}
+
+ virtual ~FactoryLoader() { closeFactory(); }
+
+ bool findFactoryForScheme(
+ int32_t CA_system_id,
+ sp<SharedLibrary> *library = NULL,
+ T** factory = NULL);
+
+ bool enumeratePlugins(vector<HidlCasPluginDescriptor>* results);
+
+private:
+ typedef T*(*CreateFactoryFunc)();
+
+ Mutex mMapLock;
+ T* mFactory;
+ const char *mCreateFactoryFuncName;
+ sp<SharedLibrary> mLibrary;
+ KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
+ KeyedVector<String8, wp<SharedLibrary> > mLibraryPathToOpenLibraryMap;
+
+ bool loadFactoryForSchemeFromPath(
+ const String8 &path,
+ int32_t CA_system_id,
+ sp<SharedLibrary> *library,
+ T** factory);
+
+ bool queryPluginsFromPath(
+ const String8 &path,
+ vector<HidlCasPluginDescriptor>* results);
+
+ bool openFactory(const String8 &path);
+ void closeFactory();
+};
+
+template <class T>
+bool FactoryLoader<T>::findFactoryForScheme(
+ int32_t CA_system_id, sp<SharedLibrary> *library, T** factory) {
+ if (library != NULL) {
+ library->clear();
+ }
+ if (factory != NULL) {
+ *factory = NULL;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ // first check cache
+ ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
+ if (index >= 0) {
+ return loadFactoryForSchemeFromPath(
+ mCASystemIdToLibraryPathMap[index],
+ CA_system_id, library, factory);
+ }
+
+ // no luck, have to search
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ if (loadFactoryForSchemeFromPath(
+ pluginPath, CA_system_id, library, factory)) {
+ mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
+ closedir(pDir);
+
+ return true;
+ }
+ }
+ }
+
+ closedir(pDir);
+
+ ALOGE("Failed to find plugin");
+ return false;
+}
+
+template <class T>
+bool FactoryLoader<T>::enumeratePlugins(
+ vector<HidlCasPluginDescriptor>* results) {
+ ALOGI("enumeratePlugins");
+
+ results->clear();
+
+ String8 dirPath("/vendor/lib/mediacas");
+ DIR* pDir = opendir(dirPath.string());
+
+ if (pDir == NULL) {
+ ALOGE("Failed to open plugin directory %s", dirPath.string());
+ return false;
+ }
+
+ Mutex::Autolock autoLock(mMapLock);
+
+ struct dirent* pEntry;
+ while ((pEntry = readdir(pDir))) {
+ String8 pluginPath = dirPath + "/" + pEntry->d_name;
+ if (pluginPath.getPathExtension() == ".so") {
+ queryPluginsFromPath(pluginPath, results);
+ }
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::loadFactoryForSchemeFromPath(
+ const String8 &path, int32_t CA_system_id,
+ sp<SharedLibrary> *library, T** factory) {
+ closeFactory();
+
+ if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
+ closeFactory();
+ return false;
+ }
+
+ if (library != NULL) {
+ *library = mLibrary;
+ }
+ if (factory != NULL) {
+ *factory = mFactory;
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::queryPluginsFromPath(
+ const String8 &path, vector<HidlCasPluginDescriptor>* results) {
+ closeFactory();
+
+ vector<CasPluginDescriptor> descriptors;
+ if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
+ closeFactory();
+ return false;
+ }
+
+ for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
+ results->push_back( HidlCasPluginDescriptor {
+ .caSystemId = it->CA_system_id,
+ .name = it->name.c_str()});
+ }
+ return true;
+}
+
+template <class T>
+bool FactoryLoader<T>::openFactory(const String8 &path) {
+ // get strong pointer to open shared library
+ ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
+ if (index >= 0) {
+ mLibrary = mLibraryPathToOpenLibraryMap[index].promote();
+ } else {
+ index = mLibraryPathToOpenLibraryMap.add(path, NULL);
+ }
+
+ if (!mLibrary.get()) {
+ mLibrary = new SharedLibrary(path);
+ if (!*mLibrary) {
+ return false;
+ }
+
+ mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
+ }
+
+ CreateFactoryFunc createFactory =
+ (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
+ if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
+ return false;
+ }
+ return true;
+}
+
+template <class T>
+void FactoryLoader<T>::closeFactory() {
+ delete mFactory;
+ mFactory = NULL;
+ mLibrary.clear();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_FACTORY_LOADER_H_
diff --git a/cas/1.0/default/MediaCasService.cpp b/cas/1.0/default/MediaCasService.cpp
new file mode 100644
index 0000000..ca43224
--- /dev/null
+++ b/cas/1.0/default/MediaCasService.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-MediaCasService"
+
+#include <android/hardware/cas/1.0/ICasListener.h>
+#include <media/cas/CasAPI.h>
+#include <media/cas/DescramblerAPI.h>
+#include <utils/Log.h>
+
+#include "CasImpl.h"
+#include "DescramblerImpl.h"
+#include "MediaCasService.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+MediaCasService::MediaCasService() :
+ mCasLoader("createCasFactory"),
+ mDescramblerLoader("createDescramblerFactory") {
+}
+
+MediaCasService::~MediaCasService() {
+}
+
+Return<void> MediaCasService::enumeratePlugins(enumeratePlugins_cb _hidl_cb) {
+
+ ALOGV("%s", __FUNCTION__);
+
+ vector<HidlCasPluginDescriptor> results;
+ mCasLoader.enumeratePlugins(&results);
+
+ _hidl_cb(results);
+ return Void();
+}
+
+Return<bool> MediaCasService::isSystemIdSupported(int32_t CA_system_id) {
+ ALOGV("isSystemIdSupported: CA_system_id=%d", CA_system_id);
+
+ return mCasLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<ICas>> MediaCasService::createPlugin(
+ int32_t CA_system_id, const sp<ICasListener>& listener) {
+
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ sp<ICas> result;
+
+ CasFactory *factory;
+ sp<SharedLibrary> library;
+ if (mCasLoader.findFactoryForScheme(CA_system_id, &library, &factory)) {
+ CasPlugin *plugin = NULL;
+ sp<CasImpl> casImpl = new CasImpl(listener);
+ if (factory->createPlugin(CA_system_id, (uint64_t)casImpl.get(),
+ &CasImpl::OnEvent, &plugin) == OK && plugin != NULL) {
+ casImpl->init(library, plugin);
+ result = casImpl;
+ }
+ }
+
+ return result;
+}
+
+Return<bool> MediaCasService::isDescramblerSupported(int32_t CA_system_id) {
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ return mDescramblerLoader.findFactoryForScheme(CA_system_id);
+}
+
+Return<sp<IDescramblerBase>> MediaCasService::createDescrambler(int32_t CA_system_id) {
+
+ ALOGV("%s: CA_system_id=%d", __FUNCTION__, CA_system_id);
+
+ sp<IDescrambler> result;
+
+ DescramblerFactory *factory;
+ sp<SharedLibrary> library;
+ if (mDescramblerLoader.findFactoryForScheme(
+ CA_system_id, &library, &factory)) {
+ DescramblerPlugin *plugin = NULL;
+ if (factory->createPlugin(CA_system_id, &plugin) == OK
+ && plugin != NULL) {
+ result = new DescramblerImpl(library, plugin);
+ }
+ }
+
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.0/default/MediaCasService.h b/cas/1.0/default/MediaCasService.h
new file mode 100644
index 0000000..77ddac6
--- /dev/null
+++ b/cas/1.0/default/MediaCasService.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_MEDIA_CAS_SERVICE_H_
+#define ANDROID_HARDWARE_CAS_V1_0_MEDIA_CAS_SERVICE_H_
+
+#include <android/hardware/cas/1.0/IMediaCasService.h>
+
+#include "FactoryLoader.h"
+
+namespace android {
+struct CasFactory;
+struct DescramblerFactory;
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+class MediaCasService : public IMediaCasService {
+public:
+ MediaCasService();
+
+ virtual Return<void> enumeratePlugins(
+ enumeratePlugins_cb _hidl_cb) override;
+
+ virtual Return<bool> isSystemIdSupported(
+ int32_t CA_system_id) override;
+
+ virtual Return<sp<ICas>> createPlugin(
+ int32_t CA_system_id, const sp<ICasListener>& listener) override;
+
+ virtual Return<bool> isDescramblerSupported(
+ int32_t CA_system_id) override;
+
+ virtual Return<sp<IDescramblerBase>> createDescrambler(
+ int32_t CA_system_id) override;
+
+private:
+ FactoryLoader<CasFactory> mCasLoader;
+ FactoryLoader<DescramblerFactory> mDescramblerLoader;
+
+ virtual ~MediaCasService();
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_MEDIA_CAS_SERVICE_H_
diff --git a/cas/1.0/default/SharedLibrary.cpp b/cas/1.0/default/SharedLibrary.cpp
new file mode 100644
index 0000000..9c7f385
--- /dev/null
+++ b/cas/1.0/default/SharedLibrary.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-SharedLibrary"
+
+#include <dlfcn.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include "SharedLibrary.h"
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+SharedLibrary::SharedLibrary(const String8 &path) {
+ mLibHandle = dlopen(path.string(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLibHandle != NULL) {
+ dlclose(mLibHandle);
+ mLibHandle = NULL;
+ }
+}
+
+bool SharedLibrary::operator!() const {
+ return mLibHandle == NULL;
+}
+
+void *SharedLibrary::lookup(const char *symbol) const {
+ if (!mLibHandle) {
+ return NULL;
+ }
+ // Clear last error before we load the symbol again,
+ // in case the caller didn't retrieve it.
+ (void)dlerror();
+ return dlsym(mLibHandle, symbol);
+}
+
+const char *SharedLibrary::lastError() const {
+ const char *error = dlerror();
+ return error ? error : "No errors or unknown error";
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.0/default/SharedLibrary.h b/cas/1.0/default/SharedLibrary.h
new file mode 100644
index 0000000..18130a5
--- /dev/null
+++ b/cas/1.0/default/SharedLibrary.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_SHARED_LIBRARY_H_
+#define ANDROID_HARDWARE_CAS_V1_0_SHARED_LIBRARY_H_
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+class SharedLibrary : public RefBase {
+public:
+ explicit SharedLibrary(const String8 &path);
+ ~SharedLibrary();
+
+ bool operator!() const;
+ void *lookup(const char *symbol) const;
+ const char *lastError() const;
+
+private:
+ void *mLibHandle;
+ DISALLOW_EVIL_CONSTRUCTORS(SharedLibrary);
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_SHARED_LIBRARY_H_
diff --git a/cas/1.0/default/TypeConvert.cpp b/cas/1.0/default/TypeConvert.cpp
new file mode 100644
index 0000000..de1f92b
--- /dev/null
+++ b/cas/1.0/default/TypeConvert.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-TypeConvert"
+
+#include <utils/Log.h>
+#include "TypeConvert.h"
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+Status toStatus(status_t legacyStatus) {
+ Status status;
+ switch(legacyStatus) {
+ case android::OK:
+ status = Status::OK;
+ break;
+ case android::ERROR_CAS_NO_LICENSE:
+ status = Status::ERROR_CAS_NO_LICENSE;
+ break;
+ case android::ERROR_CAS_LICENSE_EXPIRED:
+ status = Status::ERROR_CAS_LICENSE_EXPIRED;
+ break;
+ case android::ERROR_CAS_SESSION_NOT_OPENED:
+ status = Status::ERROR_CAS_SESSION_NOT_OPENED;
+ break;
+ case android::ERROR_CAS_CANNOT_HANDLE:
+ status = Status::ERROR_CAS_CANNOT_HANDLE;
+ break;
+ case android::ERROR_CAS_TAMPER_DETECTED:
+ status = Status::ERROR_CAS_INVALID_STATE;
+ break;
+ case android::BAD_VALUE:
+ status = Status::BAD_VALUE;
+ break;
+ case android::ERROR_CAS_NOT_PROVISIONED:
+ status = Status::ERROR_CAS_NOT_PROVISIONED;
+ break;
+ case android::ERROR_CAS_RESOURCE_BUSY:
+ status = Status::ERROR_CAS_RESOURCE_BUSY;
+ break;
+ case android::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION:
+ status = Status::ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION;
+ break;
+ case android::ERROR_CAS_DEVICE_REVOKED:
+ status = Status::ERROR_CAS_DEVICE_REVOKED;
+ break;
+ case android::ERROR_CAS_DECRYPT:
+ status = Status::ERROR_CAS_DECRYPT;
+ break;
+ default:
+ ALOGW("Unable to convert legacy status: %d, defaulting to UNKNOWN",
+ legacyStatus);
+ status = Status::ERROR_CAS_UNKNOWN;
+ break;
+ }
+ return status;
+}
+
+String8 sessionIdToString(const CasSessionId &sessionId) {
+ String8 result;
+ for (size_t i = 0; i < sessionId.size(); i++) {
+ result.appendFormat("%02x ", sessionId[i]);
+ }
+ if (result.isEmpty()) {
+ result.append("(null)");
+ }
+ return result;
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
diff --git a/cas/1.0/default/TypeConvert.h b/cas/1.0/default/TypeConvert.h
new file mode 100644
index 0000000..7c3dded
--- /dev/null
+++ b/cas/1.0/default/TypeConvert.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAS_V1_0_TYPE_CONVERT_H
+#define ANDROID_HARDWARE_CAS_V1_0_TYPE_CONVERT_H
+
+#include <android/hardware/cas/1.0/types.h>
+#include <media/cas/CasAPI.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace hardware {
+namespace cas {
+namespace V1_0 {
+namespace implementation {
+
+Status toStatus(status_t legacyStatus);
+
+String8 sessionIdToString(const CasSessionId &sessionId);
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cas
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CAS_V1_0_TYPE_CONVERT_H
diff --git a/cas/1.0/default/android.hardware.cas@1.0-service.rc b/cas/1.0/default/android.hardware.cas@1.0-service.rc
new file mode 100644
index 0000000..93de794
--- /dev/null
+++ b/cas/1.0/default/android.hardware.cas@1.0-service.rc
@@ -0,0 +1,6 @@
+service cas-hal-1-0 /vendor/bin/hw/android.hardware.cas@1.0-service
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/cas/1.0/default/service.cpp b/cas/1.0/default/service.cpp
new file mode 100644
index 0000000..3f1df5a
--- /dev/null
+++ b/cas/1.0/default/service.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "android.hardware.cas@1.0-service"
+
+#include <binder/ProcessState.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include "MediaCasService.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::cas::V1_0::implementation::MediaCasService;
+using android::hardware::cas::V1_0::IMediaCasService;
+
+int main() {
+ ALOGD("android.hardware.cas@1.0-service starting...");
+
+#ifdef USE_VNDBINDER
+ // The CAS HAL may communicate to other vendor components via
+ // /dev/vndbinder
+ android::ProcessState::initWithDriver("/dev/vndbinder");
+#endif // USE_VNDBINDER
+
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+
+ // Setup hwbinder service
+ android::sp<IMediaCasService> service = new MediaCasService();
+ android::status_t status = service->registerAsService();
+ LOG_ALWAYS_FATAL_IF(
+ status != android::OK,
+ "Error while registering cas service: %d", status);
+ joinRpcThreadpool();
+ return 0;
+}
diff --git a/cas/1.0/types.hal b/cas/1.0/types.hal
new file mode 100644
index 0000000..7337f00
--- /dev/null
+++ b/cas/1.0/types.hal
@@ -0,0 +1,124 @@
+/**
+ * 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.cas@1.0;
+
+enum Status : uint32_t {
+ /**
+ * The CAS plugin must return OK when an operation completes without any
+ * errors.
+ */
+ OK,
+
+ /**
+ * The CAS plugin must return ERROR_CAS_NO_LICENSE, when descrambling is
+ * attempted and no license keys have been provided.
+ */
+ ERROR_CAS_NO_LICENSE,
+
+ /**
+ * ERROR_CAS_LICENSE_EXPIRED must be returned when an attempt is made
+ * to use a license and the keys in that license have expired.
+ */
+ ERROR_CAS_LICENSE_EXPIRED,
+
+ /**
+ * The CAS plugin must return ERROR_CAS_SESSION_NOT_OPENED when an
+ * attempt is made to use a session that has not been opened.
+ */
+ ERROR_CAS_SESSION_NOT_OPENED,
+
+ /**
+ * The CAS plugin must return ERROR_CAS_CANNOT_HANDLE when an unsupported
+ * data format or operation is attempted.
+ */
+ ERROR_CAS_CANNOT_HANDLE,
+
+ /**
+ * ERROR_CAS_INVALID_STATE must be returned when the device is in a state
+ * where it is not able to perform descrambling.
+ */
+ ERROR_CAS_INVALID_STATE,
+
+ /**
+ * The CAS plugin must return BAD_VALUE whenever an illegal parameter is
+ * passed to one of the interface functions.
+ */
+ BAD_VALUE,
+
+ /**
+ * The CAS plugin must return ERROR_CAS_NOT_PROVISIONED when the device
+ * has not yet been provisioned.
+ */
+ ERROR_CAS_NOT_PROVISIONED,
+
+ /**
+ * ERROR_CAS_RESOURCE_BUSY must be returned when resources, such as CAS
+ * sessions or secure buffers are not available to perform a requested
+ * operation because they are already in use.
+ */
+ ERROR_CAS_RESOURCE_BUSY,
+
+ /**
+ * The CAS Plugin must return ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION
+ * when the output protection level enabled on the device is not
+ * sufficient to meet the requirements in the license policy. HDCP is an
+ * example of a form of output protection.
+ */
+ ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION,
+
+ /**
+ * The CAS Plugin must return ERROR_CAS_TAMPER_DETECTED if an attempt to
+ * tamper with the CAS system is detected.
+ */
+ ERROR_CAS_TAMPER_DETECTED,
+
+ /**
+ * The CAS Plugin must return ERROR_CAS_DEVICE_REVOKED if the response
+ * indicates that the device has been revoked. Device revocation means
+ * that the device is no longer permitted to play content.
+ */
+ ERROR_CAS_DEVICE_REVOKED,
+
+ /**
+ * The CAS plugin must return ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED when
+ * descrambling is failing because the session is not initialized properly.
+ */
+ ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED,
+
+ /**
+ * The CAS Plugin must return ERROR_CAS_DECRYPT if the DescramblerPlugin's
+ * descramble operation fails.
+ */
+ ERROR_CAS_DECRYPT,
+
+ /**
+ * ERROR_CAS_UNKNOWN must be returned when a fatal failure occurs and no
+ * other defined error is appropriate.
+ */
+ ERROR_CAS_UNKNOWN,
+};
+
+typedef vec<uint8_t> HidlCasSessionId;
+typedef vec<uint8_t> HidlCasData;
+
+/**
+ * Describes a CAS plugin with its system id and name.
+ */
+struct HidlCasPluginDescriptor {
+ int32_t caSystemId;
+ string name;
+};
diff --git a/cas/Android.bp b/cas/Android.bp
new file mode 100644
index 0000000..57532a0
--- /dev/null
+++ b/cas/Android.bp
@@ -0,0 +1,6 @@
+// This is an autogenerated file, do not edit.
+subdirs = [
+ "1.0",
+ "1.0/default",
+ "native/1.0",
+]
diff --git a/cas/native/1.0/Android.bp b/cas/native/1.0/Android.bp
new file mode 100644
index 0000000..414fca7
--- /dev/null
+++ b/cas/native/1.0/Android.bp
@@ -0,0 +1,65 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+
+filegroup {
+ name: "android.hardware.cas.native@1.0_hal",
+ srcs: [
+ "types.hal",
+ "IDescrambler.hal",
+ ],
+}
+
+genrule {
+ name: "android.hardware.cas.native@1.0_genc++",
+ tools: ["hidl-gen"],
+ cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.cas.native@1.0",
+ srcs: [
+ ":android.hardware.cas.native@1.0_hal",
+ ],
+ out: [
+ "android/hardware/cas/native/1.0/types.cpp",
+ "android/hardware/cas/native/1.0/DescramblerAll.cpp",
+ ],
+}
+
+genrule {
+ name: "android.hardware.cas.native@1.0_genc++_headers",
+ tools: ["hidl-gen"],
+ cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.cas.native@1.0",
+ srcs: [
+ ":android.hardware.cas.native@1.0_hal",
+ ],
+ out: [
+ "android/hardware/cas/native/1.0/types.h",
+ "android/hardware/cas/native/1.0/hwtypes.h",
+ "android/hardware/cas/native/1.0/IDescrambler.h",
+ "android/hardware/cas/native/1.0/IHwDescrambler.h",
+ "android/hardware/cas/native/1.0/BnHwDescrambler.h",
+ "android/hardware/cas/native/1.0/BpHwDescrambler.h",
+ "android/hardware/cas/native/1.0/BsDescrambler.h",
+ ],
+}
+
+cc_library_shared {
+ name: "android.hardware.cas.native@1.0",
+ defaults: ["hidl-module-defaults"],
+ generated_sources: ["android.hardware.cas.native@1.0_genc++"],
+ generated_headers: ["android.hardware.cas.native@1.0_genc++_headers"],
+ export_generated_headers: ["android.hardware.cas.native@1.0_genc++_headers"],
+ vendor_available: true,
+ shared_libs: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "android.hardware.cas@1.0",
+ ],
+ export_shared_lib_headers: [
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "libutils",
+ "android.hardware.cas@1.0",
+ ],
+}
diff --git a/cas/native/1.0/IDescrambler.hal b/cas/native/1.0/IDescrambler.hal
new file mode 100644
index 0000000..459e5e3
--- /dev/null
+++ b/cas/native/1.0/IDescrambler.hal
@@ -0,0 +1,45 @@
+/*
+ * 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.cas.native@1.0;
+
+import android.hardware.cas@1.0::IDescramblerBase;
+
+/**
+ * IDescrambler is the native plugin API for descrambling operations.
+ */
+
+interface IDescrambler extends IDescramblerBase {
+ /**
+ * Descramble the data in a source SharedBuffer, described by an array of
+ * SubSample structures.
+ *
+ * @param scramblingControl an enumeration indicating the key that the subsamples
+ * were scrambled with.
+ * @param subSamples an array of SubSample structures describing the number of
+ * clear and scrambled bytes within each subsample.
+ * @param srcBuffer the SharedBuffer containing the source scrambled data.
+ * @param srcOffset the position where the source scrambled data starts at.
+ * @param dstBuffer the DestinationBuffer to hold the descrambled data.
+ * @param dstOffset the position where the descrambled data should start at.
+ *
+ * @return status the status of the call.
+ * @return bytesWritten number of bytes that have been successfully descrambled.
+ * @return detailedError a detailed message describing the error (if any).
+ */
+ descramble(ScramblingControl scramblingControl, vec<SubSample> subSamples,
+ SharedBuffer srcBuffer, uint64_t srcOffset, DestinationBuffer dstBuffer, uint64_t dstOffset)
+ generates(Status status, uint32_t bytesWritten, string detailedError);
+};
diff --git a/cas/native/1.0/types.hal b/cas/native/1.0/types.hal
new file mode 100644
index 0000000..a576d03
--- /dev/null
+++ b/cas/native/1.0/types.hal
@@ -0,0 +1,90 @@
+/**
+ * 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.cas.native@1.0;
+
+import android.hardware.cas@1.0::types;
+
+/**
+ * Enumerates the keys used to scramble the content.
+ */
+enum ScramblingControl : uint32_t {
+ UNSCRAMBLED = 0,
+ RESERVED = 1,
+ EVENKEY = 2,
+ ODDKEY = 3,
+};
+
+/**
+ * A subsample consists of some number of bytes of clear (unscrambled)
+ * data followed by a number of bytes of scrambled data.
+ */
+struct SubSample {
+ uint32_t numBytesOfClearData;
+ uint32_t numBytesOfEncryptedData;
+};
+
+/**
+ * SharedBuffer describes a shared buffer which is defined by a heapBase, an
+ * offset and a size. The offset is relative to the shared memory base for the
+ * memory region identified by heapBase.
+ */
+struct SharedBuffer {
+ /**
+ * The shared memory base handle
+ */
+ memory heapBase;
+
+ /**
+ * The offset from the shared memory base
+ */
+ uint64_t offset;
+
+ /**
+ * The size of the shared buffer in bytes
+ */
+ uint64_t size;
+};
+
+/**
+ * A descrambling destination buffer can be either normal user-space shared
+ * memory for the non-secure descrambling case, or it can be a secure buffer
+ * which is referenced by a native-handle. The native handle is allocated
+ * by the vendor's buffer allocator.
+ */
+enum BufferType : uint32_t {
+ SHARED_MEMORY = 0,
+ NATIVE_HANDLE = 1,
+};
+
+struct DestinationBuffer {
+ /**
+ * The type of the buffer
+ */
+ BufferType type;
+
+ /**
+ * If type == SHARED_MEMORY, the descrambled data must be written
+ * to user-space non-secure shared memory.
+ */
+ SharedBuffer nonsecureMemory;
+
+ /**
+ * If type == NATIVE_HANDLE, the descrambled data must be written
+ * to secure memory referenced by the vendor's buffer allocator.
+ */
+ handle secureMemory;
+};
diff --git a/current.txt b/current.txt
index 59064c4..a5b5930 100644
--- a/current.txt
+++ b/current.txt
@@ -189,6 +189,7 @@
# ABI preserving changes to HALs released in Android O
+8a4082dbc7f5eef585dca841b2656ba62d6c7e10e25dd05507ead15d96224f4c android.hardware.broadcastradio@1.0::types
760485232f6cce07f8bb05e3475509956996b702f77415ee5bff05e2ec5a5bcc android.hardware.dumpstate@1.0::IDumpstateDevice
1fecfa1609ff9d27ebf761a84b4336efa9d5dac5b241f19a6663f70d8db2c4b1 android.hardware.radio@1.0::IRadioResponse
28e929b453df3d9f5060af2764e6cdb123ddb893e3e86923c877f6ff7e5f02c9 android.hardware.wifi@1.0::types
diff --git a/drm/1.0/default/Android.mk b/drm/1.0/default/Android.mk
index 0cc6e71..99773be 100644
--- a/drm/1.0/default/Android.mk
+++ b/drm/1.0/default/Android.mk
@@ -42,6 +42,9 @@
LOCAL_C_INCLUDES := \
hardware/interfaces/drm
+LOCAL_HEADER_LIBRARIES := \
+ media_plugin_headers
+
# TODO(b/18948909) Some legacy DRM plugins only support 32-bit. They need to be
# migrated to 64-bit. Once all of a device's legacy DRM plugins support 64-bit,
# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
diff --git a/drm/1.0/default/CryptoFactory.h b/drm/1.0/default/CryptoFactory.h
index 6b1d1ff..86130dc 100644
--- a/drm/1.0/default/CryptoFactory.h
+++ b/drm/1.0/default/CryptoFactory.h
@@ -20,7 +20,6 @@
#include <hidl/Status.h>
#include <media/hardware/CryptoAPI.h>
#include <PluginLoader.h>
-#include <media/SharedLibrary.h>
namespace android {
namespace hardware {
diff --git a/drm/1.0/default/DrmFactory.h b/drm/1.0/default/DrmFactory.h
index 726bf97..32e192d 100644
--- a/drm/1.0/default/DrmFactory.h
+++ b/drm/1.0/default/DrmFactory.h
@@ -20,7 +20,6 @@
#include <hidl/Status.h>
#include <media/drm/DrmAPI.h>
#include <PluginLoader.h>
-#include <media/SharedLibrary.h>
namespace android {
namespace hardware {
diff --git a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
index fe67cb9..031584b 100644
--- a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
+++ b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
@@ -568,7 +568,8 @@
android::Vector<BufferInfo>* iBuffer,
android::Vector<BufferInfo>* oBuffer,
OMX_AUDIO_CODINGTYPE eEncoding,
- OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput) {
+ OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput,
+ AudioDecHidlTest::standardComp comp) {
android::hardware::media::omx::V1_0::Status status;
Message msg;
int timeOut = TIMEOUT_COUNTER;
@@ -580,7 +581,7 @@
if (status == android::hardware::media::omx::V1_0::Status::OK) {
EXPECT_EQ(msg.type, Message::Type::EVENT);
portReconfiguration(omxNode, observer, iBuffer, oBuffer, eEncoding,
- kPortIndexInput, kPortIndexOutput, msg);
+ kPortIndexInput, kPortIndexOutput, msg, comp);
}
// status == TIMED_OUT, it could be due to process time being large
// than DEFAULT_TIMEOUT or component needs output buffers to start
@@ -789,7 +790,7 @@
(int)Info.size(), compName);
eleStream.close();
waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, eEncoding,
- kPortIndexInput, kPortIndexOutput);
+ kPortIndexInput, kPortIndexOutput, compName);
testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag);
EXPECT_EQ(timestampUslist.empty(), true);
// set state to idle
@@ -933,7 +934,7 @@
compName);
eleStream.close();
waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, eEncoding,
- kPortIndexInput, kPortIndexOutput);
+ kPortIndexInput, kPortIndexOutput, compName);
testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag);
flushPorts(omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput,
kPortIndexOutput);
@@ -949,7 +950,7 @@
compName, false);
eleStream.close();
waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, eEncoding,
- kPortIndexInput, kPortIndexOutput);
+ kPortIndexInput, kPortIndexOutput, compName);
testEOS(omxNode, observer, &iBuffer, &oBuffer, true, eosFlag);
flushPorts(omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput,
kPortIndexOutput);
@@ -1034,7 +1035,7 @@
(int)Info.size(), compName, false);
eleStream.close();
waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer, eEncoding,
- kPortIndexInput, kPortIndexOutput);
+ kPortIndexInput, kPortIndexOutput, compName);
testEOS(omxNode, observer, &iBuffer, &oBuffer, true, eosFlag);
flushPorts(omxNode, observer, &iBuffer, &oBuffer, kPortIndexInput,
kPortIndexOutput);
diff --git a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
index cd6eaf5..f6bc93a 100644
--- a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
+++ b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
@@ -521,12 +521,15 @@
// Set Default port param.
void setDefaultPortParam(sp<IOmxNode> omxNode, OMX_U32 portIndex,
OMX_VIDEO_CODINGTYPE eCompressionFormat,
+ OMX_U32 nFrameWidth, OMX_U32 nFrameHeight,
OMX_U32 nBitrate, OMX_U32 xFramerate) {
android::hardware::media::omx::V1_0::Status status;
OMX_PARAM_PORTDEFINITIONTYPE portDef;
status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
&portDef);
EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
+ portDef.format.video.nFrameWidth = nFrameWidth;
+ portDef.format.video.nFrameHeight = nFrameHeight;
portDef.format.video.nBitrate = nBitrate;
portDef.format.video.xFramerate = xFramerate;
portDef.format.video.bFlagErrorConcealment = OMX_TRUE;
@@ -1028,6 +1031,9 @@
} else if (msg.data.eventData.event == OMX_EventError) {
EXPECT_TRUE(false) << "Received OMX_EventError, not sure why";
break;
+ } else if (msg.data.eventData.event == OMX_EventDataSpaceChanged) {
+ // TODO: how am i supposed to respond now?
+ std::cout << "[ ] Info ! OMX_EventDataSpaceChanged \n";
} else {
ASSERT_TRUE(false);
}
@@ -1234,8 +1240,8 @@
// Configure output port
uint32_t nBitRate = 512000;
- setDefaultPortParam(omxNode, kPortIndexOutput, eCompressionFormat, nBitRate,
- xFramerate);
+ setDefaultPortParam(omxNode, kPortIndexOutput, eCompressionFormat,
+ nFrameWidth, nFrameHeight, nBitRate, xFramerate);
setRefreshPeriod(omxNode, kPortIndexOutput, 0);
unsigned int index;
@@ -1319,6 +1325,11 @@
setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
xFramerate, eColorFormat);
+ // Configure output port
+ uint32_t nBitRate = 512000;
+ setDefaultPortParam(omxNode, kPortIndexOutput, eCompressionFormat,
+ nFrameWidth, nFrameHeight, nBitRate, xFramerate);
+
// CreateInputSurface
EXPECT_TRUE(omx->createInputSurface(
[&](android::hardware::media::omx::V1_0::Status _s,
diff --git a/oemlock/1.0/vts/functional/Android.bp b/oemlock/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..a13b3dc
--- /dev/null
+++ b/oemlock/1.0/vts/functional/Android.bp
@@ -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.
+//
+
+cc_test {
+ name: "VtsHalOemLockV1_0TargetTest",
+ defaults: ["hidl_defaults"],
+ srcs: ["VtsHalOemLockV1_0TargetTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.oemlock@1.0",
+ ],
+ static_libs: ["VtsHalHidlTargetTestBase"],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp b/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp
new file mode 100644
index 0000000..a924fec
--- /dev/null
+++ b/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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/hardware/oemlock/1.0/IOemLock.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+using ::android::hardware::oemlock::V1_0::IOemLock;
+using ::android::hardware::oemlock::V1_0::OemLockStatus;
+using ::android::hardware::oemlock::V1_0::OemLockSecureStatus;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+
+struct OemLockHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ virtual void SetUp() override {
+ oemlock = ::testing::VtsHalHidlTargetTestBase::getService<IOemLock>();
+ ASSERT_NE(oemlock, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ sp<IOemLock> oemlock;
+};
+
+/*
+ * Check the name can be retrieved
+ */
+TEST_F(OemLockHidlTest, GetName) {
+ std::string name;
+ OemLockStatus status;
+
+ bool callbackCalled = false;
+ const auto ret = oemlock->getName([&](OemLockStatus s, hidl_string n) {
+ callbackCalled = true;
+ status = s;
+ name = n.c_str();
+ });
+
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ EXPECT_EQ(status, OemLockStatus::OK);
+ // Any value acceptable
+};
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_F(OemLockHidlTest, QueryUnlockAllowedByDevice) {
+ bool allowed;
+ OemLockStatus status;
+
+ bool callbackCalled = false;
+ const auto ret = oemlock->isOemUnlockAllowedByDevice([&](OemLockStatus s, bool a) {
+ callbackCalled = true;
+ status = s;
+ allowed = a;
+ });
+
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ EXPECT_EQ(status, OemLockStatus::OK);
+ // Any value acceptable
+}
+
+/*
+ * Check unlock allowed by device state can be toggled
+ */
+TEST_F(OemLockHidlTest, AllowedByDeviceCanBeToggled) {
+ bool allowed;
+ OemLockStatus status;
+
+ auto getAllowedCallback = [&](OemLockStatus s, bool a) {
+ status = s;
+ allowed = a;
+ };
+
+ // Get the original state so it can be restored
+ const auto get_ret = oemlock->isOemUnlockAllowedByDevice(getAllowedCallback);
+ ASSERT_TRUE(get_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+ const bool originallyAllowed = allowed;
+
+ // Toggle the state
+ const auto set_ret = oemlock->setOemUnlockAllowedByDevice(!originallyAllowed);
+ ASSERT_TRUE(set_ret.isOk());
+ ASSERT_EQ(set_ret, OemLockStatus::OK);
+ const auto check_set_ret = oemlock->isOemUnlockAllowedByDevice(getAllowedCallback);
+ ASSERT_TRUE(check_set_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+ ASSERT_EQ(allowed, !originallyAllowed);
+
+ // Restore the state
+ const auto restore_ret = oemlock->setOemUnlockAllowedByDevice(originallyAllowed);
+ ASSERT_TRUE(restore_ret.isOk());
+ ASSERT_EQ(restore_ret, OemLockStatus::OK);
+ const auto check_restore_ret = oemlock->isOemUnlockAllowedByDevice(getAllowedCallback);
+ ASSERT_TRUE(check_restore_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+ ASSERT_EQ(allowed, originallyAllowed);
+};
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_F(OemLockHidlTest, QueryUnlockAllowedByCarrier) {
+ bool allowed;
+ OemLockStatus status;
+
+ bool callbackCalled = false;
+ const auto ret = oemlock->isOemUnlockAllowedByCarrier([&](OemLockStatus s, bool a) {
+ callbackCalled = true;
+ status = s;
+ allowed = a;
+ });
+
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ EXPECT_EQ(status, OemLockStatus::OK);
+ // Any value acceptable
+}
+
+/*
+ * Attempt to check unlock allowed by carrier can be toggled
+ *
+ * The implementation may involve a signature which cannot be tested here. That
+ * is a valid implementation so the test will pass. If there is no signature
+ * required, the test will toggle the value.
+ */
+TEST_F(OemLockHidlTest, CarrierUnlock) {
+ const hidl_vec<uint8_t> noSignature = {};
+ bool allowed;
+ OemLockStatus status;
+
+ auto getAllowedCallback = [&](OemLockStatus s, bool a) {
+ status = s;
+ allowed = a;
+ };
+
+ // Get the original state so it can be restored
+ const auto get_ret = oemlock->isOemUnlockAllowedByCarrier(getAllowedCallback);
+ ASSERT_TRUE(get_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+ const bool originallyAllowed = allowed;
+
+ if (originallyAllowed) {
+ // Only applied to locked devices
+ return;
+ }
+
+ // Toggle the state
+ const auto set_ret = oemlock->setOemUnlockAllowedByCarrier(!originallyAllowed, noSignature);
+ ASSERT_TRUE(set_ret.isOk());
+ ASSERT_NE(set_ret, OemLockSecureStatus::FAILED);
+ const auto check_set_ret = oemlock->isOemUnlockAllowedByCarrier(getAllowedCallback);
+ ASSERT_TRUE(check_set_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+
+ if (set_ret == OemLockSecureStatus::INVALID_SIGNATURE) {
+ // Signature is required so we cannot toggle the value in the test, but this is allowed
+ ASSERT_EQ(allowed, originallyAllowed);
+ return;
+ }
+
+ ASSERT_EQ(set_ret, OemLockSecureStatus::OK);
+ ASSERT_EQ(allowed, !originallyAllowed);
+
+ // Restore the state
+ const auto restore_ret = oemlock->setOemUnlockAllowedByCarrier(originallyAllowed, noSignature);
+ ASSERT_TRUE(restore_ret.isOk());
+ ASSERT_EQ(restore_ret, OemLockSecureStatus::OK);
+ const auto check_restore_ret = oemlock->isOemUnlockAllowedByCarrier(getAllowedCallback);
+ ASSERT_TRUE(check_restore_ret.isOk());
+ ASSERT_EQ(status, OemLockStatus::OK);
+ ASSERT_EQ(allowed, originallyAllowed);
+};
diff --git a/oemlock/Android.bp b/oemlock/Android.bp
index bbb3e4b..33f70eb 100644
--- a/oemlock/Android.bp
+++ b/oemlock/Android.bp
@@ -1,4 +1,5 @@
// This is an autogenerated file, do not edit.
subdirs = [
"1.0",
+ "1.0/vts/functional",
]
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp b/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
index 70d6187..13c23a8 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
@@ -651,6 +651,7 @@
EXPECT_EQ(serial, radioRsp->rspInfo.serial);
if (cardStatus.cardState == CardState::ABSENT) {
+ std::cout << static_cast<int>(radioRsp->rspInfo.error) << std::endl;
ASSERT_TRUE(CheckGeneralError() ||
radioRsp->rspInfo.error == RadioError::INVALID_ARGUMENTS ||
radioRsp->rspInfo.error == RadioError::INVALID_STATE);
@@ -669,6 +670,7 @@
EXPECT_EQ(serial, radioRsp->rspInfo.serial);
if (cardStatus.cardState == CardState::ABSENT) {
+ std::cout << static_cast<int>(radioRsp->rspInfo.error) << std::endl;
ASSERT_TRUE(radioRsp->rspInfo.error == RadioError::RADIO_NOT_AVAILABLE ||
radioRsp->rspInfo.error == RadioError::LCE_NOT_SUPPORTED ||
radioRsp->rspInfo.error == RadioError::INTERNAL_ERR);
@@ -776,7 +778,7 @@
EXPECT_EQ(serial, radioRsp->rspInfo.serial);
if (cardStatus.cardState == CardState::ABSENT) {
- ASSERT_TRUE(radioRsp->rspInfo.error == RadioError::NONE);
+ EXPECT_EQ(radioRsp->rspInfo.error, RadioError::NONE);
}
}
@@ -826,6 +828,7 @@
EXPECT_EQ(serial, radioRsp->rspInfo.serial);
if (cardStatus.cardState == CardState::ABSENT) {
+ std::cout << static_cast<int>(radioRsp->rspInfo.error) << std::endl;
ASSERT_TRUE(radioRsp->rspInfo.error == RadioError::NONE ||
radioRsp->rspInfo.error == RadioError::REQUEST_NOT_SUPPORTED);
}
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 6655fc7..f5be572 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_voice.cpp
@@ -366,6 +366,7 @@
EXPECT_EQ(serial, radioRsp->rspInfo.serial);
if (cardStatus.cardState == CardState::ABSENT) {
+ std::cout << static_cast<int>(radioRsp->rspInfo.error) << std::endl;
ASSERT_TRUE(CheckGeneralError() ||
radioRsp->rspInfo.error == RadioError::INVALID_ARGUMENTS ||
radioRsp->rspInfo.error == RadioError::NONE ||
diff --git a/sensors/1.0/default/Android.bp b/sensors/1.0/default/Android.bp
index 8144590..4bbafa6 100644
--- a/sensors/1.0/default/Android.bp
+++ b/sensors/1.0/default/Android.bp
@@ -38,6 +38,9 @@
"android.hardware.sensors@1.0",
],
local_include_dirs: ["include/sensors"],
+ export_shared_lib_headers: [
+ "libhardware",
+ ],
}
diff --git a/weaver/1.0/vts/functional/Android.bp b/weaver/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..9b0ff6d
--- /dev/null
+++ b/weaver/1.0/vts/functional/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalWeaverV1_0TargetTest",
+ defaults: ["hidl_defaults"],
+ srcs: ["VtsHalWeaverV1_0TargetTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.weaver@1.0",
+ ],
+ static_libs: ["VtsHalHidlTargetTestBase"],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
new file mode 100644
index 0000000..372d787
--- /dev/null
+++ b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * 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/hardware/weaver/1.0/IWeaver.h>
+
+#include <limits>
+
+#include <VtsHalHidlTargetTestBase.h>
+
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::Return;
+using ::android::sp;
+
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
+
+struct WeaverHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+ virtual void SetUp() override {
+ weaver = ::testing::VtsHalHidlTargetTestBase::getService<IWeaver>();
+ ASSERT_NE(weaver, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ sp<IWeaver> weaver;
+};
+
+/*
+ * Checks config values are suitably large
+ */
+TEST_F(WeaverHidlTest, GetConfig) {
+ WeaverStatus status;
+ WeaverConfig config;
+
+ bool callbackCalled = false;
+ auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ EXPECT_GE(config.slots, 16u);
+ EXPECT_GE(config.keySize, 16u);
+ EXPECT_GE(config.valueSize, 16u);
+}
+
+/*
+ * Gets the config twice and checks they are the same
+ */
+TEST_F(WeaverHidlTest, GettingConfigMultipleTimesGivesSameResult) {
+ WeaverConfig config1;
+ WeaverConfig config2;
+
+ WeaverStatus status;
+ bool callbackCalled = false;
+ auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ config1 = c;
+ });
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ callbackCalled = false;
+ ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ config2 = c;
+ });
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ EXPECT_EQ(config1, config2);
+}
+
+/*
+ * Gets the number of slots from the config and writes a key and value to the last one
+ */
+TEST_F(WeaverHidlTest, WriteToLastSlot) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ const uint32_t lastSlot = config.slots - 1;
+ const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+ ASSERT_EQ(writeRet, WeaverStatus::OK);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with the same key and receives the value that was previously written
+ */
+TEST_F(WeaverHidlTest, WriteFollowedByReadGivesTheSameValue) {
+ constexpr uint32_t slotId = 0;
+ const auto ret = weaver->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(ret, WeaverStatus::OK);
+
+ bool callbackCalled = false;
+ WeaverReadStatus status;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ ASSERT_TRUE(readRet.isOk());
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_EQ(status, WeaverReadStatus::OK);
+ EXPECT_EQ(readValue, VALUE);
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Overwrites the slot with a new key and value
+ * Reads the slot with the new key and receives the new value
+ */
+TEST_F(WeaverHidlTest, OverwritingSlotUpdatesTheValue) {
+ constexpr uint32_t slotId = 0;
+ const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
+ ASSERT_TRUE(initialWriteRet.isOk());
+ ASSERT_EQ(initialWriteRet, WeaverStatus::OK);
+
+ const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
+ ASSERT_TRUE(overwriteRet.isOk());
+ ASSERT_EQ(overwriteRet, WeaverStatus::OK);
+
+ bool callbackCalled = false;
+ WeaverReadStatus status;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ ASSERT_TRUE(readRet.isOk());
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_EQ(status, WeaverReadStatus::OK);
+ EXPECT_EQ(readValue, OTHER_VALUE);
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with a different key so does not receive the value
+ */
+TEST_F(WeaverHidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
+ constexpr uint32_t slotId = 0;
+ const auto ret = weaver->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(ret, WeaverStatus::OK);
+
+ bool callbackCalled = false;
+ WeaverReadStatus status;
+ std::vector<uint8_t> readValue;
+ const auto readRet =
+ weaver->read(slotId, WRONG_KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ readValue = r.value;
+ });
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_TRUE(readRet.isOk());
+ ASSERT_EQ(status, WeaverReadStatus::INCORRECT_KEY);
+ EXPECT_TRUE(readValue.empty());
+}
+
+/*
+ * Writing to an invalid slot fails
+ */
+TEST_F(WeaverHidlTest, WritingToInvalidSlotFails) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ if (config.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ const auto writeRet = weaver->write(config.slots, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+ ASSERT_EQ(writeRet, WeaverStatus::FAILED);
+}
+
+/*
+ * Reading from an invalid slot fails rather than incorrect key
+ */
+TEST_F(WeaverHidlTest, ReadingFromInvalidSlotFails) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ if (config.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ bool callbackCalled = false;
+ WeaverReadStatus readStatus;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet =
+ weaver->read(config.slots, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ readStatus = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_TRUE(readRet.isOk());
+ ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
+ EXPECT_TRUE(readValue.empty());
+ EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writing a key that is too large fails
+ */
+TEST_F(WeaverHidlTest, WriteWithTooLargeKeyFails) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ std::vector<uint8_t> bigKey(config.keySize + 1);
+
+ constexpr uint32_t slotId = 0;
+ const auto writeRet = weaver->write(slotId, bigKey, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+ ASSERT_EQ(writeRet, WeaverStatus::FAILED);
+}
+
+/*
+ * Writing a value that is too large fails
+ */
+TEST_F(WeaverHidlTest, WriteWithTooLargeValueFails) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ std::vector<uint8_t> bigValue(config.valueSize + 1);
+
+ constexpr uint32_t slotId = 0;
+ const auto writeRet = weaver->write(slotId, KEY, bigValue);
+ ASSERT_TRUE(writeRet.isOk());
+ ASSERT_EQ(writeRet, WeaverStatus::FAILED);
+}
+
+/*
+ * Reading with a key that is loo large fails
+ */
+TEST_F(WeaverHidlTest, ReadWithTooLargeKeyFails) {
+ WeaverStatus status;
+ WeaverConfig config;
+ const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ status = s;
+ config = c;
+ });
+ ASSERT_TRUE(configRet.isOk());
+ ASSERT_EQ(status, WeaverStatus::OK);
+
+ std::vector<uint8_t> bigKey(config.keySize + 1);
+
+ constexpr uint32_t slotId = 0;
+ bool callbackCalled = false;
+ WeaverReadStatus readStatus;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ const auto readRet =
+ weaver->read(slotId, bigKey, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ readStatus = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ ASSERT_TRUE(callbackCalled);
+ ASSERT_TRUE(readRet.isOk());
+ ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
+ EXPECT_TRUE(readValue.empty());
+ EXPECT_EQ(timeout, 0u);
+}
diff --git a/weaver/Android.bp b/weaver/Android.bp
index bbb3e4b..33f70eb 100644
--- a/weaver/Android.bp
+++ b/weaver/Android.bp
@@ -1,4 +1,5 @@
// This is an autogenerated file, do not edit.
subdirs = [
"1.0",
+ "1.0/vts/functional",
]