1/*
2**
3** Copyright (C) 2015, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "Radio"
19//#define LOG_NDEBUG 0
20
21#include <utils/Log.h>
22#include <utils/threads.h>
23#include <binder/IPCThreadState.h>
24#include <binder/IServiceManager.h>
25#include <binder/IMemory.h>
26
27#include <radio/Radio.h>
28#include <radio/IRadio.h>
29#include <radio/IRadioService.h>
30#include <radio/IRadioClient.h>
31#include <radio/RadioCallback.h>
32
33namespace android {
34
35namespace {
36    sp<IRadioService>          gRadioService;
37    const int                  kRadioServicePollDelay = 500000; // 0.5s
38    const char*                kRadioServiceName      = "media.radio";
39    Mutex                      gLock;
40
41    class DeathNotifier : public IBinder::DeathRecipient
42    {
43    public:
44        DeathNotifier() {
45        }
46
47        virtual void binderDied(const wp<IBinder>& who __unused) {
48            ALOGV("binderDied");
49            Mutex::Autolock _l(gLock);
50            gRadioService.clear();
51            ALOGW("Radio service died!");
52        }
53    };
54
55    sp<DeathNotifier>         gDeathNotifier;
56}; // namespace anonymous
57
58const sp<IRadioService> Radio::getRadioService()
59{
60    Mutex::Autolock _l(gLock);
61    if (gRadioService.get() == 0) {
62        sp<IServiceManager> sm = defaultServiceManager();
63        sp<IBinder> binder;
64        do {
65            binder = sm->getService(String16(kRadioServiceName));
66            if (binder != 0) {
67                break;
68            }
69            ALOGW("RadioService not published, waiting...");
70            usleep(kRadioServicePollDelay);
71        } while(true);
72        if (gDeathNotifier == NULL) {
73            gDeathNotifier = new DeathNotifier();
74        }
75        binder->linkToDeath(gDeathNotifier);
76        gRadioService = interface_cast<IRadioService>(binder);
77    }
78    ALOGE_IF(gRadioService == 0, "no RadioService!?");
79    return gRadioService;
80}
81
82// Static methods
83status_t Radio::listModules(struct radio_properties *properties,
84                            uint32_t *numModules)
85{
86    ALOGV("listModules()");
87    const sp<IRadioService> service = getRadioService();
88    if (service == 0) {
89        return NO_INIT;
90    }
91    return service->listModules(properties, numModules);
92}
93
94sp<Radio> Radio::attach(radio_handle_t handle,
95                        const struct radio_band_config *config,
96                        bool withAudio,
97                        const sp<RadioCallback>& callback)
98{
99    ALOGV("attach()");
100    sp<Radio> radio;
101    const sp<IRadioService> service = getRadioService();
102    if (service == 0) {
103        return radio;
104    }
105    radio = new Radio(handle, callback);
106    status_t status = service->attach(handle, radio, config, withAudio, radio->mIRadio);
107
108    if (status == NO_ERROR && radio->mIRadio != 0) {
109        IInterface::asBinder(radio->mIRadio)->linkToDeath(radio);
110    } else {
111        ALOGW("Error %d connecting to radio service", status);
112        radio.clear();
113    }
114    return radio;
115}
116
117
118
119// Radio
120Radio::Radio(radio_handle_t handle, const sp<RadioCallback>& callback)
121    : mHandle(handle), mCallback(callback)
122{
123}
124
125Radio::~Radio()
126{
127    if (mIRadio != 0) {
128        mIRadio->detach();
129    }
130}
131
132
133void Radio::detach() {
134    ALOGV("detach()");
135    Mutex::Autolock _l(mLock);
136    mCallback.clear();
137    if (mIRadio != 0) {
138        mIRadio->detach();
139        IInterface::asBinder(mIRadio)->unlinkToDeath(this);
140        mIRadio = 0;
141    }
142}
143
144status_t Radio::setConfiguration(const struct radio_band_config *config)
145{
146    Mutex::Autolock _l(mLock);
147    if (mIRadio == 0) {
148        return NO_INIT;
149    }
150    return mIRadio->setConfiguration(config);
151}
152
153status_t Radio::getConfiguration(struct radio_band_config *config)
154{
155    Mutex::Autolock _l(mLock);
156    if (mIRadio == 0) {
157        return NO_INIT;
158    }
159    return mIRadio->getConfiguration(config);
160}
161
162status_t Radio::setMute(bool mute)
163{
164    Mutex::Autolock _l(mLock);
165    if (mIRadio == 0) {
166        return NO_INIT;
167    }
168    return mIRadio->setMute(mute);
169}
170
171status_t Radio::getMute(bool *mute)
172{
173    Mutex::Autolock _l(mLock);
174    if (mIRadio == 0) {
175        return NO_INIT;
176    }
177    return mIRadio->getMute(mute);
178}
179
180status_t Radio::scan(radio_direction_t direction, bool skipSubchannel)
181{
182    Mutex::Autolock _l(mLock);
183    if (mIRadio == 0) {
184        return NO_INIT;
185    }
186    return mIRadio->scan(direction, skipSubchannel);
187}
188
189status_t Radio::step(radio_direction_t direction, bool skipSubchannel)
190{
191    Mutex::Autolock _l(mLock);
192    if (mIRadio == 0) {
193        return NO_INIT;
194    }
195    return mIRadio->step(direction, skipSubchannel);
196}
197
198status_t Radio::tune(unsigned int channel, unsigned int subChannel)
199{
200    Mutex::Autolock _l(mLock);
201    if (mIRadio == 0) {
202        return NO_INIT;
203    }
204    return mIRadio->tune(channel, subChannel);
205}
206
207status_t Radio::cancel()
208{
209    Mutex::Autolock _l(mLock);
210    if (mIRadio == 0) {
211        return NO_INIT;
212    }
213    return mIRadio->cancel();
214}
215
216status_t Radio::getProgramInformation(struct radio_program_info *info)
217{
218    Mutex::Autolock _l(mLock);
219    if (mIRadio == 0) {
220        return NO_INIT;
221    }
222    return mIRadio->getProgramInformation(info);
223}
224
225status_t Radio::hasControl(bool *hasControl)
226{
227    Mutex::Autolock _l(mLock);
228    if (mIRadio == 0) {
229        return NO_INIT;
230    }
231    return mIRadio->hasControl(hasControl);
232}
233
234
235// BpRadioClient
236void Radio::onEvent(const sp<IMemory>& eventMemory)
237{
238    Mutex::Autolock _l(mLock);
239    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
240        return;
241    }
242
243    // The event layout in shared memory is:
244    // sizeof(struct radio_event) bytes : the event itself
245    // 4 bytes                          : metadata size or 0
246    // N bytes                          : metadata if present
247    struct radio_event *event = (struct radio_event *)eventMemory->pointer();
248    uint32_t metadataOffset = sizeof(struct radio_event) + sizeof(uint32_t);
249    uint32_t metadataSize = *(uint32_t *)((uint8_t *)event + metadataOffset - sizeof(uint32_t));
250
251    // restore local metadata pointer from offset
252    switch (event->type) {
253    case RADIO_EVENT_TUNED:
254    case RADIO_EVENT_AF_SWITCH:
255        if (metadataSize != 0) {
256            event->info.metadata =
257                    (radio_metadata_t *)((uint8_t *)event + metadataOffset);
258        } else {
259            event->info.metadata = 0;
260        }
261        break;
262    case RADIO_EVENT_METADATA:
263        if (metadataSize != 0) {
264            event->metadata =
265                    (radio_metadata_t *)((uint8_t *)event + metadataOffset);
266        } else {
267            event->metadata = 0;
268        }
269        break;
270    default:
271        break;
272    }
273
274    if (mCallback != 0) {
275        mCallback->onEvent(event);
276    }
277}
278
279
280//IBinder::DeathRecipient
281void Radio::binderDied(const wp<IBinder>& who __unused) {
282    Mutex::Autolock _l(mLock);
283    ALOGW("Radio server binder Died ");
284    mIRadio = 0;
285    struct radio_event event;
286    memset(&event, 0, sizeof(struct radio_event));
287    event.type = RADIO_EVENT_SERVER_DIED;
288    event.status = DEAD_OBJECT;
289    if (mCallback != 0) {
290        mCallback->onEvent(&event);
291    }
292}
293
294}; // namespace android
295