283 lines
7 KiB
C++
283 lines
7 KiB
C++
/*
|
|
**
|
|
** Copyright (C) 2015, The Android Open Source Project
|
|
**
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
**
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "Radio"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <utils/Log.h>
|
|
#include <utils/threads.h>
|
|
#include <binder/IPCThreadState.h>
|
|
#include <binder/IServiceManager.h>
|
|
#include <binder/IMemory.h>
|
|
|
|
#include <radio/Radio.h>
|
|
#include <radio/IRadio.h>
|
|
#include <radio/IRadioService.h>
|
|
#include <radio/IRadioClient.h>
|
|
#include <radio/RadioCallback.h>
|
|
|
|
namespace android {
|
|
|
|
namespace {
|
|
sp<IRadioService> gRadioService;
|
|
const int kRadioServicePollDelay = 500000; // 0.5s
|
|
const char* kRadioServiceName = "media.radio";
|
|
Mutex gLock;
|
|
|
|
class DeathNotifier : public IBinder::DeathRecipient
|
|
{
|
|
public:
|
|
DeathNotifier() {
|
|
}
|
|
|
|
virtual void binderDied(const wp<IBinder>& who __unused) {
|
|
ALOGV("binderDied");
|
|
Mutex::Autolock _l(gLock);
|
|
gRadioService.clear();
|
|
ALOGW("Radio service died!");
|
|
}
|
|
};
|
|
|
|
sp<DeathNotifier> gDeathNotifier;
|
|
}; // namespace anonymous
|
|
|
|
const sp<IRadioService> Radio::getRadioService()
|
|
{
|
|
Mutex::Autolock _l(gLock);
|
|
if (gRadioService.get() == 0) {
|
|
sp<IServiceManager> sm = defaultServiceManager();
|
|
sp<IBinder> binder;
|
|
do {
|
|
binder = sm->getService(String16(kRadioServiceName));
|
|
if (binder != 0) {
|
|
break;
|
|
}
|
|
ALOGW("RadioService not published, waiting...");
|
|
usleep(kRadioServicePollDelay);
|
|
} while(true);
|
|
if (gDeathNotifier == NULL) {
|
|
gDeathNotifier = new DeathNotifier();
|
|
}
|
|
binder->linkToDeath(gDeathNotifier);
|
|
gRadioService = interface_cast<IRadioService>(binder);
|
|
}
|
|
ALOGE_IF(gRadioService == 0, "no RadioService!?");
|
|
return gRadioService;
|
|
}
|
|
|
|
// Static methods
|
|
status_t Radio::listModules(struct radio_properties *properties,
|
|
uint32_t *numModules)
|
|
{
|
|
ALOGV("listModules()");
|
|
const sp<IRadioService> service = getRadioService();
|
|
if (service == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return service->listModules(properties, numModules);
|
|
}
|
|
|
|
sp<Radio> Radio::attach(radio_handle_t handle,
|
|
const struct radio_band_config *config,
|
|
bool withAudio,
|
|
const sp<RadioCallback>& callback)
|
|
{
|
|
ALOGV("attach()");
|
|
sp<Radio> radio;
|
|
const sp<IRadioService> service = getRadioService();
|
|
if (service == 0) {
|
|
return radio;
|
|
}
|
|
radio = new Radio(handle, callback);
|
|
status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio);
|
|
|
|
if (status == NO_ERROR && radio->mIRadio != 0) {
|
|
IInterface::asBinder(radio->mIRadio)->linkToDeath(radio);
|
|
} else {
|
|
ALOGW("Error %d connecting to radio service", status);
|
|
radio.clear();
|
|
}
|
|
return radio;
|
|
}
|
|
|
|
|
|
|
|
// Radio
|
|
Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback)
|
|
: mHandle(handle), mCallback(callback)
|
|
{
|
|
}
|
|
|
|
Radio::~Radio()
|
|
{
|
|
if (mIRadio != 0) {
|
|
mIRadio->detach();
|
|
}
|
|
}
|
|
|
|
|
|
void Radio::detach() {
|
|
ALOGV("detach()");
|
|
Mutex::Autolock _l(mLock);
|
|
mCallback.clear();
|
|
if (mIRadio != 0) {
|
|
mIRadio->detach();
|
|
IInterface::asBinder(mIRadio)->unlinkToDeath(this);
|
|
mIRadio = 0;
|
|
}
|
|
}
|
|
|
|
status_t Radio::setConfiguration(const struct radio_band_config *config)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->setConfiguration(config);
|
|
}
|
|
|
|
status_t Radio::getConfiguration(struct radio_band_config *config)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->getConfiguration(config);
|
|
}
|
|
|
|
status_t Radio::setMute(bool mute)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->setMute(mute);
|
|
}
|
|
|
|
status_t Radio::getMute(bool *mute)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->getMute(mute);
|
|
}
|
|
|
|
status_t Radio::scan(radio_direction_t direction, bool skipSubchannel)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->scan(direction, skipSubchannel);
|
|
}
|
|
|
|
status_t Radio::step(radio_direction_t direction, bool skipSubchannel)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->step(direction, skipSubchannel);
|
|
}
|
|
|
|
status_t Radio::tune(unsigned int channel, unsigned int subChannel)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->tune(channel, subChannel);
|
|
}
|
|
|
|
status_t Radio::cancel()
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->cancel();
|
|
}
|
|
|
|
status_t Radio::getProgramInformation(struct radio_program_info *info)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->getProgramInformation(info);
|
|
}
|
|
|
|
status_t Radio::hasControl(bool *hasControl)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mIRadio == 0) {
|
|
return NO_INIT;
|
|
}
|
|
return mIRadio->hasControl(hasControl);
|
|
}
|
|
|
|
|
|
// BpRadioClient
|
|
void Radio::onEvent(const sp<IMemory>& eventMemory)
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (eventMemory == 0 || eventMemory->pointer() == NULL) {
|
|
return;
|
|
}
|
|
|
|
struct radio_event *event = (struct radio_event *)eventMemory->pointer();
|
|
// restore local metadata pointer from offset
|
|
switch (event->type) {
|
|
case RADIO_EVENT_TUNED:
|
|
case RADIO_EVENT_AF_SWITCH:
|
|
if (event->info.metadata != NULL) {
|
|
event->info.metadata =
|
|
(radio_metadata_t *)((char *)event + (size_t)event->info.metadata);
|
|
}
|
|
break;
|
|
case RADIO_EVENT_METADATA:
|
|
if (event->metadata != NULL) {
|
|
event->metadata =
|
|
(radio_metadata_t *)((char *)event + (size_t)event->metadata);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (mCallback != 0) {
|
|
mCallback->onEvent(event);
|
|
}
|
|
}
|
|
|
|
|
|
//IBinder::DeathRecipient
|
|
void Radio::binderDied(const wp<IBinder>& who __unused) {
|
|
Mutex::Autolock _l(mLock);
|
|
ALOGW("Radio server binder Died ");
|
|
mIRadio = 0;
|
|
struct radio_event event;
|
|
memset(&event, 0, sizeof(struct radio_event));
|
|
event.type = RADIO_EVENT_SERVER_DIED;
|
|
event.status = DEAD_OBJECT;
|
|
if (mCallback != 0) {
|
|
mCallback->onEvent(&event);
|
|
}
|
|
}
|
|
|
|
}; // namespace android
|