You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
11 KiB
338 lines
11 KiB
/*
|
|
**
|
|
** Copyright 2015, The Android Open Source Project
|
|
**
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
**
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "IRadio"
|
|
//#define LOG_NDEBUG 0
|
|
#include <utils/Log.h>
|
|
#include <utils/Errors.h>
|
|
#include <binder/IMemory.h>
|
|
#include <radio/IRadio.h>
|
|
#include <radio/IRadioService.h>
|
|
#include <radio/IRadioClient.h>
|
|
#include <system/radio.h>
|
|
#include <system/RadioMetadataWrapper.h>
|
|
|
|
namespace android {
|
|
|
|
enum {
|
|
DETACH = IBinder::FIRST_CALL_TRANSACTION,
|
|
SET_CONFIGURATION,
|
|
GET_CONFIGURATION,
|
|
SET_MUTE,
|
|
GET_MUTE,
|
|
SCAN,
|
|
STEP,
|
|
TUNE,
|
|
CANCEL,
|
|
GET_PROGRAM_INFORMATION,
|
|
HAS_CONTROL
|
|
};
|
|
|
|
class BpRadio: public BpInterface<IRadio>
|
|
{
|
|
public:
|
|
explicit BpRadio(const sp<IBinder>& impl)
|
|
: BpInterface<IRadio>(impl)
|
|
{
|
|
}
|
|
|
|
void detach()
|
|
{
|
|
ALOGV("detach");
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
remote()->transact(DETACH, data, &reply);
|
|
}
|
|
|
|
virtual status_t setConfiguration(const struct radio_band_config *config)
|
|
{
|
|
Parcel data, reply;
|
|
if (config == NULL) {
|
|
return BAD_VALUE;
|
|
}
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
data.write(config, sizeof(struct radio_band_config));
|
|
status_t status = remote()->transact(SET_CONFIGURATION, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t getConfiguration(struct radio_band_config *config)
|
|
{
|
|
Parcel data, reply;
|
|
if (config == NULL) {
|
|
return BAD_VALUE;
|
|
}
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
status_t status = remote()->transact(GET_CONFIGURATION, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
if (status == NO_ERROR) {
|
|
reply.read(config, sizeof(struct radio_band_config));
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t setMute(bool mute)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
data.writeInt32(mute ? 1 : 0);
|
|
status_t status = remote()->transact(SET_MUTE, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t getMute(bool *mute)
|
|
{
|
|
Parcel data, reply;
|
|
if (mute == NULL) {
|
|
return BAD_VALUE;
|
|
}
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
status_t status = remote()->transact(GET_MUTE, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
if (status == NO_ERROR) {
|
|
int32_t muteread = reply.readInt32();
|
|
*mute = muteread != 0;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t scan(radio_direction_t direction, bool skipSubChannel)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
data.writeInt32(direction);
|
|
data.writeInt32(skipSubChannel ? 1 : 0);
|
|
status_t status = remote()->transact(SCAN, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t step(radio_direction_t direction, bool skipSubChannel)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
data.writeInt32(direction);
|
|
data.writeInt32(skipSubChannel ? 1 : 0);
|
|
status_t status = remote()->transact(STEP, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t tune(uint32_t channel, uint32_t subChannel)
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
data.writeUint32(channel);
|
|
data.writeUint32(subChannel);
|
|
status_t status = remote()->transact(TUNE, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t cancel()
|
|
{
|
|
Parcel data, reply;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
status_t status = remote()->transact(CANCEL, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t getProgramInformation(struct radio_program_info *info)
|
|
{
|
|
Parcel data, reply;
|
|
if (info == nullptr || info->metadata == nullptr) {
|
|
return BAD_VALUE;
|
|
}
|
|
radio_metadata_t *metadata = info->metadata;
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
status_t status = remote()->transact(GET_PROGRAM_INFORMATION, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
if (status == NO_ERROR) {
|
|
reply.read(info, sizeof(struct radio_program_info));
|
|
// restore local metadata pointer
|
|
info->metadata = metadata;
|
|
|
|
uint32_t metadataSize = reply.readUint32();
|
|
if (metadataSize != 0) {
|
|
radio_metadata_t *newMetadata = (radio_metadata_t *)malloc(metadataSize);
|
|
if (newMetadata == NULL) {
|
|
return NO_MEMORY;
|
|
}
|
|
reply.read(newMetadata, metadataSize);
|
|
status = radio_metadata_add_metadata(&info->metadata, newMetadata);
|
|
free(newMetadata);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
virtual status_t hasControl(bool *hasControl)
|
|
{
|
|
Parcel data, reply;
|
|
if (hasControl == NULL) {
|
|
return BAD_VALUE;
|
|
}
|
|
data.writeInterfaceToken(IRadio::getInterfaceDescriptor());
|
|
status_t status = remote()->transact(HAS_CONTROL, data, &reply);
|
|
if (status == NO_ERROR) {
|
|
status = (status_t)reply.readInt32();
|
|
if (status == NO_ERROR) {
|
|
*hasControl = reply.readInt32() != 0;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_META_INTERFACE(Radio, "android.hardware.IRadio");
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
status_t BnRadio::onTransact(
|
|
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
|
|
{
|
|
switch(code) {
|
|
case DETACH: {
|
|
ALOGV("DETACH");
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
detach();
|
|
return NO_ERROR;
|
|
} break;
|
|
case SET_CONFIGURATION: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
struct radio_band_config config;
|
|
data.read(&config, sizeof(struct radio_band_config));
|
|
status_t status = setConfiguration(&config);
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case GET_CONFIGURATION: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
struct radio_band_config config;
|
|
status_t status = getConfiguration(&config);
|
|
reply->writeInt32(status);
|
|
if (status == NO_ERROR) {
|
|
reply->write(&config, sizeof(struct radio_band_config));
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
case SET_MUTE: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
bool mute = data.readInt32() != 0;
|
|
status_t status = setMute(mute);
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case GET_MUTE: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
bool mute;
|
|
status_t status = getMute(&mute);
|
|
reply->writeInt32(status);
|
|
if (status == NO_ERROR) {
|
|
reply->writeInt32(mute ? 1 : 0);
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
case SCAN: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
radio_direction_t direction = (radio_direction_t)data.readInt32();
|
|
bool skipSubChannel = data.readInt32() == 1;
|
|
status_t status = scan(direction, skipSubChannel);
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case STEP: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
radio_direction_t direction = (radio_direction_t)data.readInt32();
|
|
bool skipSubChannel = data.readInt32() == 1;
|
|
status_t status = step(direction, skipSubChannel);
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case TUNE: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
uint32_t channel = data.readUint32();
|
|
uint32_t subChannel = data.readUint32();
|
|
status_t status = tune(channel, subChannel);
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case CANCEL: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
status_t status = cancel();
|
|
reply->writeInt32(status);
|
|
return NO_ERROR;
|
|
}
|
|
case GET_PROGRAM_INFORMATION: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
struct radio_program_info info;
|
|
RadioMetadataWrapper metadataWrapper(&info.metadata);
|
|
|
|
status_t status = getProgramInformation(&info);
|
|
reply->writeInt32(status);
|
|
if (status == NO_ERROR) {
|
|
reply->write(&info, sizeof(struct radio_program_info));
|
|
if (radio_metadata_get_count(info.metadata) > 0) {
|
|
size_t size = radio_metadata_get_size(info.metadata);
|
|
reply->writeUint32((uint32_t)size);
|
|
reply->write(info.metadata, size);
|
|
} else {
|
|
reply->writeUint32(0);
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
case HAS_CONTROL: {
|
|
CHECK_INTERFACE(IRadio, data, reply);
|
|
bool control;
|
|
status_t status = hasControl(&control);
|
|
reply->writeInt32(status);
|
|
if (status == NO_ERROR) {
|
|
reply->writeInt32(control ? 1 : 0);
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
default:
|
|
return BBinder::onTransact(code, data, reply, flags);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
}; // namespace android
|