RadioService.cpp revision 4e09069a29fc18d0799808cc26f71e9b068e98ad
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "RadioService"
18//#define LOG_NDEBUG 0
19
20#include <stdio.h>
21#include <string.h>
22#include <sys/types.h>
23#include <pthread.h>
24
25#include <system/radio.h>
26#include <system/radio_metadata.h>
27#include <cutils/atomic.h>
28#include <cutils/properties.h>
29#include <hardware/hardware.h>
30#include <utils/Errors.h>
31#include <utils/Log.h>
32#include <binder/IServiceManager.h>
33#include <binder/MemoryBase.h>
34#include <binder/MemoryHeapBase.h>
35#include <hardware/radio.h>
36#include "RadioService.h"
37#include "RadioRegions.h"
38
39namespace android {
40
41
42RadioService::RadioService()
43    : BnRadioService(), mNextUniqueId(1)
44{
45    ALOGI("%s", __FUNCTION__);
46}
47
48void RadioService::onFirstRef()
49{
50    const hw_module_t *mod;
51    int rc;
52    struct radio_hw_device *dev;
53
54    ALOGI("%s", __FUNCTION__);
55
56    rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod);
57    if (rc != 0) {
58        ALOGE("couldn't load radio module %s.%s (%s)",
59              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
60        return;
61    }
62    rc = radio_hw_device_open(mod, &dev);
63    if (rc != 0) {
64        ALOGE("couldn't open radio hw device in %s.%s (%s)",
65              RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc));
66        return;
67    }
68    if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) {
69        ALOGE("wrong radio hw device version %04x", dev->common.version);
70        return;
71    }
72
73    struct radio_hal_properties halProperties;
74    rc = dev->get_properties(dev, &halProperties);
75    if (rc != 0) {
76        ALOGE("could not read implementation properties");
77        return;
78    }
79
80    radio_properties_t properties;
81    properties.handle =
82            (radio_handle_t)android_atomic_inc(&mNextUniqueId);
83
84    ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);
85
86    convertProperties(&properties, &halProperties);
87    sp<Module> module = new Module(this, dev, properties);
88    mModules.add(properties.handle, module);
89}
90
91RadioService::~RadioService()
92{
93    for (size_t i = 0; i < mModules.size(); i++) {
94        radio_hw_device_close(mModules.valueAt(i)->hwDevice());
95    }
96}
97
98status_t RadioService::listModules(struct radio_properties *properties,
99                             uint32_t *numModules)
100{
101    ALOGV("listModules");
102
103    AutoMutex lock(mServiceLock);
104    if (numModules == NULL || (*numModules != 0 && properties == NULL)) {
105        return BAD_VALUE;
106    }
107    size_t maxModules = *numModules;
108    *numModules = mModules.size();
109    for (size_t i = 0; i < mModules.size() && i < maxModules; i++) {
110        properties[i] = mModules.valueAt(i)->properties();
111    }
112    return NO_ERROR;
113}
114
115status_t RadioService::attach(radio_handle_t handle,
116                        const sp<IRadioClient>& client,
117                        const struct radio_band_config *config,
118                        bool withAudio,
119                        sp<IRadio>& radio)
120{
121    ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio);
122
123    AutoMutex lock(mServiceLock);
124    radio.clear();
125    if (client == 0) {
126        return BAD_VALUE;
127    }
128    ssize_t index = mModules.indexOfKey(handle);
129    if (index < 0) {
130        return BAD_VALUE;
131    }
132    sp<Module> module = mModules.valueAt(index);
133
134    if (config == NULL) {
135        config = module->getDefaultConfig();
136        if (config == NULL) {
137            return INVALID_OPERATION;
138        }
139    }
140    ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type);
141
142    radio = module->addClient(client, config, withAudio);
143
144    if (radio == 0) {
145        NO_INIT;
146    }
147    return NO_ERROR;
148}
149
150
151static const int kDumpLockRetries = 50;
152static const int kDumpLockSleep = 60000;
153
154static bool tryLock(Mutex& mutex)
155{
156    bool locked = false;
157    for (int i = 0; i < kDumpLockRetries; ++i) {
158        if (mutex.tryLock() == NO_ERROR) {
159            locked = true;
160            break;
161        }
162        usleep(kDumpLockSleep);
163    }
164    return locked;
165}
166
167status_t RadioService::dump(int fd, const Vector<String16>& args __unused) {
168    String8 result;
169    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
170        result.appendFormat("Permission Denial: can't dump RadioService");
171        write(fd, result.string(), result.size());
172    } else {
173        bool locked = tryLock(mServiceLock);
174        // failed to lock - RadioService is probably deadlocked
175        if (!locked) {
176            result.append("RadioService may be deadlocked\n");
177            write(fd, result.string(), result.size());
178        }
179
180        if (locked) mServiceLock.unlock();
181    }
182    return NO_ERROR;
183}
184
185status_t RadioService::onTransact(
186    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
187    return BnRadioService::onTransact(code, data, reply, flags);
188}
189
190
191// static
192void RadioService::callback(radio_hal_event_t *halEvent, void *cookie)
193{
194    CallbackThread *callbackThread = (CallbackThread *)cookie;
195    if (callbackThread == NULL) {
196        return;
197    }
198    callbackThread->sendEvent(halEvent);
199}
200
201/* static */
202void RadioService::convertProperties(radio_properties_t *properties,
203                                     const radio_hal_properties_t *halProperties)
204{
205    memset(properties, 0, sizeof(struct radio_properties));
206    properties->class_id = halProperties->class_id;
207    strlcpy(properties->implementor, halProperties->implementor,
208            RADIO_STRING_LEN_MAX);
209    strlcpy(properties->product, halProperties->product,
210            RADIO_STRING_LEN_MAX);
211    strlcpy(properties->version, halProperties->version,
212            RADIO_STRING_LEN_MAX);
213    strlcpy(properties->serial, halProperties->serial,
214            RADIO_STRING_LEN_MAX);
215    properties->num_tuners = halProperties->num_tuners;
216    properties->num_audio_sources = halProperties->num_audio_sources;
217    properties->supports_capture = halProperties->supports_capture;
218
219    for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) {
220        const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band;
221        size_t j;
222        for (j = 0; j < halProperties->num_bands; j++) {
223            const radio_hal_band_config_t *halBand = &halProperties->bands[j];
224            size_t k;
225            if (band->type != halBand->type) continue;
226            if (band->lower_limit < halBand->lower_limit) continue;
227            if (band->upper_limit > halBand->upper_limit) continue;
228            for (k = 0; k < halBand->num_spacings; k++) {
229                if (band->spacings[0] == halBand->spacings[k]) break;
230            }
231            if (k == halBand->num_spacings) continue;
232            if (band->type == RADIO_BAND_AM) break;
233            if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue;
234            if (halBand->fm.rds == 0) break;
235            if ((band->fm.rds & halBand->fm.rds) != 0) break;
236        }
237        if (j == halProperties->num_bands) continue;
238
239        ALOGI("convertProperties() Adding band type %d region %d",
240              sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region);
241
242        memcpy(&properties->bands[properties->num_bands++],
243               &sKnownRegionConfigs[i],
244               sizeof(radio_band_config_t));
245    }
246}
247
248#undef LOG_TAG
249#define LOG_TAG "RadioService::CallbackThread"
250
251RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient)
252    : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService"))
253{
254}
255
256RadioService::CallbackThread::~CallbackThread()
257{
258    mEventQueue.clear();
259}
260
261void RadioService::CallbackThread::onFirstRef()
262{
263    run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO);
264}
265
266bool RadioService::CallbackThread::threadLoop()
267{
268    while (!exitPending()) {
269        sp<IMemory> eventMemory;
270        sp<ModuleClient> moduleClient;
271        {
272            Mutex::Autolock _l(mCallbackLock);
273            while (mEventQueue.isEmpty() && !exitPending()) {
274                ALOGV("CallbackThread::threadLoop() sleep");
275                mCallbackCond.wait(mCallbackLock);
276                ALOGV("CallbackThread::threadLoop() wake up");
277            }
278            if (exitPending()) {
279                break;
280            }
281            eventMemory = mEventQueue[0];
282            mEventQueue.removeAt(0);
283            moduleClient = mModuleClient.promote();
284        }
285        if (moduleClient != 0) {
286            moduleClient->onCallbackEvent(eventMemory);
287            eventMemory.clear();
288        }
289    }
290    return false;
291}
292
293void RadioService::CallbackThread::exit()
294{
295    Mutex::Autolock _l(mCallbackLock);
296    requestExit();
297    mCallbackCond.broadcast();
298}
299
300sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent)
301{
302    sp<IMemory> eventMemory;
303
304    size_t headerSize =
305            (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int);
306    size_t metadataSize = 0;
307    switch (halEvent->type) {
308    case RADIO_EVENT_TUNED:
309    case RADIO_EVENT_AF_SWITCH:
310        if (radio_metadata_check(halEvent->info.metadata) == 0) {
311            metadataSize = radio_metadata_get_size(halEvent->info.metadata);
312        }
313        break;
314    case RADIO_EVENT_METADATA:
315        if (radio_metadata_check(halEvent->metadata) != 0) {
316            return eventMemory;
317        }
318        metadataSize = radio_metadata_get_size(halEvent->metadata);
319        break;
320    default:
321        break;
322    }
323    size_t size = headerSize + metadataSize;
324    eventMemory = mMemoryDealer->allocate(size);
325    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
326        eventMemory.clear();
327        return eventMemory;
328    }
329    struct radio_event *event = (struct radio_event *)eventMemory->pointer();
330    event->type = halEvent->type;
331    event->status = halEvent->status;
332
333    switch (event->type) {
334    case RADIO_EVENT_CONFIG:
335        event->config.band = halEvent->config;
336        break;
337    case RADIO_EVENT_TUNED:
338    case RADIO_EVENT_AF_SWITCH:
339        event->info = halEvent->info;
340        if (metadataSize != 0) {
341            memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize);
342            // replace meta data pointer by offset while in shared memory so that receiving side
343            // can restore the pointer in destination process.
344            event->info.metadata = (radio_metadata_t *)headerSize;
345        }
346        break;
347    case RADIO_EVENT_TA:
348    case RADIO_EVENT_ANTENNA:
349    case RADIO_EVENT_CONTROL:
350        event->on = halEvent->on;
351        break;
352    case RADIO_EVENT_METADATA:
353        memcpy((char *)event + headerSize, halEvent->metadata, metadataSize);
354        // replace meta data pointer by offset while in shared memory so that receiving side
355        // can restore the pointer in destination process.
356        event->metadata = (radio_metadata_t *)headerSize;
357        break;
358    case RADIO_EVENT_HW_FAILURE:
359    default:
360        break;
361    }
362
363    return eventMemory;
364}
365
366void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event)
367 {
368     sp<IMemory> eventMemory = prepareEvent(event);
369     if (eventMemory == 0) {
370         return;
371     }
372
373     AutoMutex lock(mCallbackLock);
374     mEventQueue.add(eventMemory);
375     mCallbackCond.signal();
376     ALOGV("%s DONE", __FUNCTION__);
377}
378
379
380#undef LOG_TAG
381#define LOG_TAG "RadioService::Module"
382
383RadioService::Module::Module(const sp<RadioService>& service,
384                                      radio_hw_device* hwDevice,
385                                      radio_properties properties)
386 : mService(service), mHwDevice(hwDevice), mProperties(properties), mMute(true)
387{
388}
389
390RadioService::Module::~Module() {
391    mModuleClients.clear();
392}
393
394status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) {
395    String8 result;
396    return NO_ERROR;
397}
398
399sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client,
400                                    const struct radio_band_config *config,
401                                    bool audio)
402{
403    ALOGV("addClient() %p config %p product %s", this, config, mProperties.product);
404    AutoMutex lock(mLock);
405    sp<ModuleClient> moduleClient;
406    int ret;
407
408    for (size_t i = 0; i < mModuleClients.size(); i++) {
409        if (mModuleClients[i]->client() == client) {
410            // client already connected: reject
411            return moduleClient;
412        }
413    }
414    moduleClient = new ModuleClient(this, client, config, audio);
415
416    struct radio_hal_band_config halConfig;
417    halConfig = config->band;
418
419    sp<ModuleClient> oldestTuner;
420    sp<ModuleClient> oldestAudio;
421    size_t allocatedTuners = 0;
422    size_t allocatedAudio = 0;
423    for (size_t i = 0; i < mModuleClients.size(); i++) {
424        if (mModuleClients[i]->getTuner() != NULL) {
425            if (mModuleClients[i]->audio()) {
426                if (oldestAudio == 0) {
427                    oldestAudio = mModuleClients[i];
428                }
429                allocatedAudio++;
430            } else {
431                if (oldestTuner == 0) {
432                    oldestTuner = mModuleClients[i];
433                }
434                allocatedTuners++;
435            }
436        }
437    }
438
439    const struct radio_tuner *halTuner;
440    if (audio) {
441        if (allocatedAudio >= mProperties.num_audio_sources) {
442            ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
443            halTuner = oldestAudio->getTuner();
444            oldestAudio->setTuner(NULL);
445            mHwDevice->close_tuner(mHwDevice, halTuner);
446        }
447    } else {
448        if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) {
449            if (allocatedTuners != 0) {
450                ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch");
451                halTuner = oldestTuner->getTuner();
452                oldestTuner->setTuner(NULL);
453                mHwDevice->close_tuner(mHwDevice, halTuner);
454            } else {
455                ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
456                halTuner = oldestAudio->getTuner();
457                oldestAudio->setTuner(NULL);
458                mHwDevice->close_tuner(mHwDevice, halTuner);
459            }
460        }
461    }
462
463    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
464                                RadioService::callback, moduleClient->callbackThread().get(),
465                                &halTuner);
466    if (ret == 0) {
467        ALOGV("addClient() setTuner %p", halTuner);
468        moduleClient->setTuner(halTuner);
469        mModuleClients.add(moduleClient);
470    } else {
471        moduleClient.clear();
472    }
473
474    //TODO notify audio device connection to audio policy manager if audio is on
475
476    ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
477
478    return moduleClient;
479}
480
481void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) {
482    ALOGV("removeClient()");
483    AutoMutex lock(mLock);
484    int ret;
485    ssize_t index = -1;
486
487    for (size_t i = 0; i < mModuleClients.size(); i++) {
488        if (mModuleClients[i] == moduleClient) {
489            index = i;
490            break;
491        }
492    }
493    if (index == -1) {
494        return;
495    }
496
497    mModuleClients.removeAt(index);
498    const struct radio_tuner *halTuner = moduleClient->getTuner();
499    if (halTuner == NULL) {
500        return;
501    }
502
503    mHwDevice->close_tuner(mHwDevice, halTuner);
504
505    //TODO notify audio device disconnection to audio policy manager if audio was on
506    mMute = true;
507
508    if (mModuleClients.isEmpty()) {
509        return;
510    }
511
512    sp<ModuleClient> youngestClient;
513    sp<ModuleClient> youngestClientAudio;
514    size_t allocatedTuners = 0;
515    size_t allocatedAudio = 0;
516    for (ssize_t i = mModuleClients.size(); i >= 0; i--) {
517        if (mModuleClients[i]->getTuner() == NULL) {
518            if (mModuleClients[i]->audio()) {
519                if (youngestClientAudio == 0) {
520                    youngestClientAudio = mModuleClients[i];
521                }
522            } else {
523                if (youngestClient == 0) {
524                    youngestClient = mModuleClients[i];
525                }
526            }
527        } else {
528            if (mModuleClients[i]->audio()) {
529                allocatedAudio++;
530            } else {
531                allocatedTuners++;
532            }
533        }
534    }
535
536    ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners,
537                "removeClient() removed client but no tuner available");
538
539    ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources,
540                "removeClient() removed audio client but no tuner with audio available");
541
542    if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) {
543        youngestClient = youngestClientAudio;
544    }
545
546    ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner");
547
548    struct radio_hal_band_config halConfig = youngestClient->halConfig();
549    ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(),
550                                RadioService::callback, moduleClient->callbackThread().get(),
551                                &halTuner);
552
553    //TODO notify audio device connection to audio policy manager if audio is on
554
555    if (ret == 0) {
556        youngestClient->setTuner(halTuner);
557    }
558}
559
560status_t RadioService::Module::setMute(bool mute)
561{
562    Mutex::Autolock _l(mLock);
563    if (mute != mMute) {
564        mMute = mute;
565        //TODO notifify audio policy manager of media activity on radio audio device
566    }
567    return NO_ERROR;
568}
569
570status_t RadioService::Module::getMute(bool *mute)
571{
572    Mutex::Autolock _l(mLock);
573    *mute = mMute;
574    return NO_ERROR;
575}
576
577
578const struct radio_band_config *RadioService::Module::getDefaultConfig() const
579{
580    if (mProperties.num_bands == 0) {
581        return NULL;
582    }
583    return &mProperties.bands[0];
584}
585
586#undef LOG_TAG
587#define LOG_TAG "RadioService::ModuleClient"
588
589RadioService::ModuleClient::ModuleClient(const sp<Module>& module,
590                                         const sp<IRadioClient>& client,
591                                         const struct radio_band_config *config,
592                                         bool audio)
593 : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL)
594{
595}
596
597void RadioService::ModuleClient::onFirstRef()
598{
599    mCallbackThread = new CallbackThread(this);
600    IInterface::asBinder(mClient)->linkToDeath(this);
601}
602
603RadioService::ModuleClient::~ModuleClient() {
604    if (mClient != 0) {
605        IInterface::asBinder(mClient)->unlinkToDeath(this);
606        mClient.clear();
607    }
608    if (mCallbackThread != 0) {
609        mCallbackThread->exit();
610    }
611}
612
613status_t RadioService::ModuleClient::dump(int fd __unused,
614                                             const Vector<String16>& args __unused) {
615    String8 result;
616    return NO_ERROR;
617}
618
619void RadioService::ModuleClient::detach() {
620    ALOGV("%s", __FUNCTION__);
621    sp<ModuleClient> strongMe = this;
622    {
623        AutoMutex lock(mLock);
624        if (mClient != 0) {
625            IInterface::asBinder(mClient)->unlinkToDeath(this);
626            mClient.clear();
627        }
628    }
629    sp<Module> module = mModule.promote();
630    if (module == 0) {
631        return;
632    }
633    module->removeClient(this);
634}
635
636radio_hal_band_config_t RadioService::ModuleClient::halConfig() const
637{
638    AutoMutex lock(mLock);
639    ALOGV("%s locked", __FUNCTION__);
640    return mConfig.band;
641}
642
643const struct radio_tuner *RadioService::ModuleClient::getTuner() const
644{
645    AutoMutex lock(mLock);
646    ALOGV("%s locked", __FUNCTION__);
647    return mTuner;
648}
649
650void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner)
651{
652    ALOGV("%s %p", __FUNCTION__, this);
653
654    AutoMutex lock(mLock);
655    mTuner = tuner;
656    ALOGV("%s locked", __FUNCTION__);
657
658    radio_hal_event_t event;
659    event.type = RADIO_EVENT_CONTROL;
660    event.status = 0;
661    event.on = mTuner != NULL;
662    mCallbackThread->sendEvent(&event);
663    ALOGV("%s DONE", __FUNCTION__);
664
665}
666
667status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config)
668{
669    AutoMutex lock(mLock);
670    status_t status = NO_ERROR;
671    ALOGV("%s locked", __FUNCTION__);
672
673    if (mTuner != NULL) {
674        struct radio_hal_band_config halConfig;
675        halConfig = config->band;
676        status = (status_t)mTuner->set_configuration(mTuner, &halConfig);
677        if (status == NO_ERROR) {
678            mConfig = *config;
679        }
680    } else {
681        mConfig = *config;
682        status == INVALID_OPERATION;
683    }
684
685    return status;
686}
687
688status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config)
689{
690    AutoMutex lock(mLock);
691    status_t status = NO_ERROR;
692    ALOGV("%s locked", __FUNCTION__);
693
694    if (mTuner != NULL) {
695        struct radio_hal_band_config halConfig;
696        status = (status_t)mTuner->get_configuration(mTuner, &halConfig);
697        if (status == NO_ERROR) {
698            mConfig.band = halConfig;
699        }
700    }
701    *config = mConfig;
702
703    return status;
704}
705
706status_t RadioService::ModuleClient::setMute(bool mute)
707{
708    sp<Module> module;
709    {
710        Mutex::Autolock _l(mLock);
711        ALOGV("%s locked", __FUNCTION__);
712        if (mTuner == NULL || !mAudio) {
713            return INVALID_OPERATION;
714        }
715        module = mModule.promote();
716        if (module == 0) {
717            return NO_INIT;
718        }
719    }
720    module->setMute(mute);
721    return NO_ERROR;
722}
723
724status_t RadioService::ModuleClient::getMute(bool *mute)
725{
726    sp<Module> module;
727    {
728        Mutex::Autolock _l(mLock);
729        ALOGV("%s locked", __FUNCTION__);
730        module = mModule.promote();
731        if (module == 0) {
732            return NO_INIT;
733        }
734    }
735    return module->getMute(mute);
736}
737
738status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel)
739{
740    AutoMutex lock(mLock);
741    ALOGV("%s locked", __FUNCTION__);
742    status_t status;
743    if (mTuner != NULL) {
744        status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel);
745    } else {
746        status = INVALID_OPERATION;
747    }
748    return status;
749}
750
751status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel)
752{
753    AutoMutex lock(mLock);
754    ALOGV("%s locked", __FUNCTION__);
755    status_t status;
756    if (mTuner != NULL) {
757        status = (status_t)mTuner->step(mTuner, direction, skipSubChannel);
758    } else {
759        status = INVALID_OPERATION;
760    }
761    return status;
762}
763
764status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel)
765{
766    AutoMutex lock(mLock);
767    ALOGV("%s locked", __FUNCTION__);
768    status_t status;
769    if (mTuner != NULL) {
770        status = (status_t)mTuner->tune(mTuner, channel, subChannel);
771    } else {
772        status = INVALID_OPERATION;
773    }
774    return status;
775}
776
777status_t RadioService::ModuleClient::cancel()
778{
779    AutoMutex lock(mLock);
780    ALOGV("%s locked", __FUNCTION__);
781    status_t status;
782    if (mTuner != NULL) {
783        status = (status_t)mTuner->cancel(mTuner);
784    } else {
785        status = INVALID_OPERATION;
786    }
787    return status;
788}
789
790status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info)
791{
792    AutoMutex lock(mLock);
793    ALOGV("%s locked", __FUNCTION__);
794    status_t status;
795    if (mTuner != NULL) {
796        status = (status_t)mTuner->get_program_information(mTuner, info);
797    } else {
798        status = INVALID_OPERATION;
799    }
800    return status;
801}
802
803status_t RadioService::ModuleClient::hasControl(bool *hasControl)
804{
805    Mutex::Autolock lock(mLock);
806    ALOGV("%s locked", __FUNCTION__);
807    *hasControl = mTuner != NULL;
808    return NO_ERROR;
809}
810
811void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory)
812{
813    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
814        return;
815    }
816
817    sp<IRadioClient> client;
818    {
819        AutoMutex lock(mLock);
820        ALOGV("%s locked", __FUNCTION__);
821        radio_event_t *event = (radio_event_t *)eventMemory->pointer();
822        switch (event->type) {
823        case RADIO_EVENT_CONFIG:
824            mConfig.band = event->config.band;
825            event->config.region = mConfig.region;
826            break;
827        default:
828            break;
829        }
830
831        client = mClient;
832    }
833    if (client != 0) {
834        client->onEvent(eventMemory);
835    }
836}
837
838
839void RadioService::ModuleClient::binderDied(
840    const wp<IBinder> &who __unused) {
841    ALOGW("client binder died for client %p", this);
842    detach();
843}
844
845}; // namespace android
846