1/*
2 * Copyright (C) 2016 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 "RadioHalHidl"
18//#define LOG_NDEBUG 0
19
20#include <media/audiohal/hidl/HalDeathHandler.h>
21#include <utils/Log.h>
22#include <utils/misc.h>
23#include <system/RadioMetadataWrapper.h>
24#include <android/hardware/broadcastradio/1.0/IBroadcastRadioFactory.h>
25
26#include "RadioHalHidl.h"
27#include "HidlUtils.h"
28
29namespace android {
30
31using android::hardware::broadcastradio::V1_0::IBroadcastRadioFactory;
32using android::hardware::broadcastradio::V1_0::Class;
33using android::hardware::broadcastradio::V1_0::Direction;
34using android::hardware::broadcastradio::V1_0::Properties;
35
36
37/* static */
38sp<RadioInterface> RadioInterface::connectModule(radio_class_t classId)
39{
40    return new RadioHalHidl(classId);
41}
42
43int RadioHalHidl::getProperties(radio_hal_properties_t *properties)
44{
45    ALOGV("%s IN", __FUNCTION__);
46    sp<IBroadcastRadio> module = getService();
47    if (module == 0) {
48        return -ENODEV;
49    }
50    Properties halProperties;
51    Result halResult = Result::NOT_INITIALIZED;
52    Return<void> hidlReturn =
53            module->getProperties([&](Result result, const Properties& properties) {
54                    halResult = result;
55                    if (result == Result::OK) {
56                        halProperties = properties;
57                    }
58                });
59
60    if (halResult == Result::OK) {
61        HidlUtils::convertPropertiesFromHal(properties, &halProperties);
62    }
63    return HidlUtils::convertHalResult(halResult);
64}
65
66int RadioHalHidl::openTuner(const radio_hal_band_config_t *config,
67                            bool audio,
68                            sp<TunerCallbackInterface> callback,
69                            sp<TunerInterface>& tuner)
70{
71    sp<IBroadcastRadio> module = getService();
72    if (module == 0) {
73        return -ENODEV;
74    }
75    sp<Tuner> tunerImpl = new Tuner(callback, this);
76
77    BandConfig halConfig;
78    Result halResult = Result::NOT_INITIALIZED;
79    sp<ITuner> halTuner;
80
81    HidlUtils::convertBandConfigToHal(&halConfig, config);
82    Return<void> hidlReturn =
83            module->openTuner(halConfig, audio, tunerImpl,
84                              [&](Result result, const sp<ITuner>& tuner) {
85                    halResult = result;
86                    if (result == Result::OK) {
87                        halTuner = tuner;
88                    }
89                });
90
91    if (halResult == Result::OK) {
92        tunerImpl->setHalTuner(halTuner);
93        tuner = tunerImpl;
94    }
95
96    return HidlUtils::convertHalResult(halResult);
97}
98
99int RadioHalHidl::closeTuner(sp<TunerInterface>& tuner)
100{
101    sp<Tuner> tunerImpl = static_cast<Tuner *>(tuner.get());
102    sp<ITuner> clearTuner;
103    tunerImpl->setHalTuner(clearTuner);
104    return 0;
105}
106
107RadioHalHidl::RadioHalHidl(radio_class_t classId)
108    : mClassId(classId)
109{
110}
111
112RadioHalHidl::~RadioHalHidl()
113{
114}
115
116sp<IBroadcastRadio> RadioHalHidl::getService()
117{
118    if (mHalModule == 0) {
119        sp<IBroadcastRadioFactory> factory = IBroadcastRadioFactory::getService();
120        if (factory != 0) {
121            factory->connectModule(static_cast<Class>(mClassId),
122                               [&](Result retval, const ::android::sp<IBroadcastRadio>& result) {
123                if (retval == Result::OK) {
124                    mHalModule = result;
125                }
126            });
127        }
128    }
129    ALOGV("%s OUT module %p", __FUNCTION__, mHalModule.get());
130    return mHalModule;
131}
132
133void RadioHalHidl::clearService()
134{
135    ALOGV("%s IN module %p", __FUNCTION__, mHalModule.get());
136    mHalModule.clear();
137}
138
139
140int RadioHalHidl::Tuner::setConfiguration(const radio_hal_band_config_t *config)
141{
142    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
143
144    if (mHalTuner == 0) {
145        return -ENODEV;
146    }
147    BandConfig halConfig;
148    HidlUtils::convertBandConfigToHal(&halConfig, config);
149
150    Return<Result> hidlResult = mHalTuner->setConfiguration(halConfig);
151    return HidlUtils::convertHalResult(hidlResult);
152}
153
154int RadioHalHidl::Tuner::getConfiguration(radio_hal_band_config_t *config)
155{
156    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
157    if (mHalTuner == 0) {
158        return -ENODEV;
159    }
160    BandConfig halConfig;
161    Result halResult;
162    Return<void> hidlReturn =
163            mHalTuner->getConfiguration([&](Result result, const BandConfig& config) {
164                    halResult = result;
165                    if (result == Result::OK) {
166                        halConfig = config;
167                    }
168                });
169    if (hidlReturn.isOk() && halResult == Result::OK) {
170        HidlUtils::convertBandConfigFromHal(config, &halConfig);
171    }
172    return HidlUtils::convertHalResult(halResult);
173}
174
175int RadioHalHidl::Tuner::scan(radio_direction_t direction, bool skip_sub_channel)
176{
177    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
178    if (mHalTuner == 0) {
179        return -ENODEV;
180    }
181    Return<Result> hidlResult =
182            mHalTuner->scan(static_cast<Direction>(direction), skip_sub_channel);
183    return HidlUtils::convertHalResult(hidlResult);
184}
185
186int RadioHalHidl::Tuner::step(radio_direction_t direction, bool skip_sub_channel)
187{
188    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
189    if (mHalTuner == 0) {
190        return -ENODEV;
191    }
192    Return<Result> hidlResult =
193            mHalTuner->step(static_cast<Direction>(direction), skip_sub_channel);
194    return HidlUtils::convertHalResult(hidlResult);
195}
196
197int RadioHalHidl::Tuner::tune(unsigned int channel, unsigned int sub_channel)
198{
199    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
200    if (mHalTuner == 0) {
201        return -ENODEV;
202    }
203    Return<Result> hidlResult =
204            mHalTuner->tune(channel, sub_channel);
205    return HidlUtils::convertHalResult(hidlResult);
206}
207
208int RadioHalHidl::Tuner::cancel()
209{
210    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
211    if (mHalTuner == 0) {
212        return -ENODEV;
213    }
214    Return<Result> hidlResult = mHalTuner->cancel();
215    return HidlUtils::convertHalResult(hidlResult);
216}
217
218int RadioHalHidl::Tuner::getProgramInformation(radio_program_info_t *info)
219{
220    ALOGV("%s IN mHalTuner %p", __FUNCTION__, mHalTuner.get());
221    if (mHalTuner == 0) {
222        return -ENODEV;
223    }
224    if (info == nullptr || info->metadata == nullptr) {
225        return BAD_VALUE;
226    }
227    ProgramInfo halInfo;
228    Result halResult;
229    Return<void> hidlReturn = mHalTuner->getProgramInformation(
230        [&](Result result, const ProgramInfo& info) {
231            halResult = result;
232            if (result == Result::OK) {
233                halInfo = info;
234            }
235        });
236    if (hidlReturn.isOk() && halResult == Result::OK) {
237        HidlUtils::convertProgramInfoFromHal(info, &halInfo);
238    }
239    return HidlUtils::convertHalResult(halResult);
240}
241
242Return<void> RadioHalHidl::Tuner::hardwareFailure()
243{
244    ALOGV("%s IN", __FUNCTION__);
245    handleHwFailure();
246    return Return<void>();
247}
248
249Return<void> RadioHalHidl::Tuner::configChange(Result result, const BandConfig& config)
250{
251    ALOGV("%s IN", __FUNCTION__);
252    radio_hal_event_t event;
253    memset(&event, 0, sizeof(radio_hal_event_t));
254    event.type = RADIO_EVENT_CONFIG;
255    event.status = HidlUtils::convertHalResult(result);
256    HidlUtils::convertBandConfigFromHal(&event.config, &config);
257    onCallback(&event);
258    return Return<void>();
259}
260
261Return<void> RadioHalHidl::Tuner::tuneComplete(Result result, const ProgramInfo& info)
262{
263    ALOGV("%s IN", __FUNCTION__);
264    radio_hal_event_t event = {};
265    RadioMetadataWrapper metadataWrapper(&event.info.metadata);
266
267    event.type = RADIO_EVENT_TUNED;
268    event.status = HidlUtils::convertHalResult(result);
269    HidlUtils::convertProgramInfoFromHal(&event.info, &info);
270    onCallback(&event);
271    return Return<void>();
272}
273
274Return<void> RadioHalHidl::Tuner::afSwitch(const ProgramInfo& info)
275{
276    ALOGV("%s IN", __FUNCTION__);
277    radio_hal_event_t event = {};
278    RadioMetadataWrapper metadataWrapper(&event.info.metadata);
279
280    event.type = RADIO_EVENT_AF_SWITCH;
281    HidlUtils::convertProgramInfoFromHal(&event.info, &info);
282    onCallback(&event);
283    return Return<void>();
284}
285
286Return<void> RadioHalHidl::Tuner::antennaStateChange(bool connected)
287{
288    ALOGV("%s IN", __FUNCTION__);
289    radio_hal_event_t event;
290    memset(&event, 0, sizeof(radio_hal_event_t));
291    event.type = RADIO_EVENT_ANTENNA;
292    event.on = connected;
293    onCallback(&event);
294    return Return<void>();
295}
296Return<void> RadioHalHidl::Tuner::trafficAnnouncement(bool active)
297{
298    ALOGV("%s IN", __FUNCTION__);
299    radio_hal_event_t event;
300    memset(&event, 0, sizeof(radio_hal_event_t));
301    event.type = RADIO_EVENT_TA;
302    event.on = active;
303    onCallback(&event);
304    return Return<void>();
305}
306Return<void> RadioHalHidl::Tuner::emergencyAnnouncement(bool active)
307{
308    ALOGV("%s IN", __FUNCTION__);
309    radio_hal_event_t event;
310    memset(&event, 0, sizeof(radio_hal_event_t));
311    event.type = RADIO_EVENT_EA;
312    event.on = active;
313    onCallback(&event);
314    return Return<void>();
315}
316Return<void> RadioHalHidl::Tuner::newMetadata(uint32_t channel, uint32_t subChannel,
317                                          const ::android::hardware::hidl_vec<MetaData>& metadata)
318{
319    ALOGV("%s IN", __FUNCTION__);
320    radio_hal_event_t event = {};
321    RadioMetadataWrapper metadataWrapper(&event.metadata);
322
323    event.type = RADIO_EVENT_METADATA;
324    HidlUtils::convertMetaDataFromHal(&event.metadata, metadata, channel, subChannel);
325    onCallback(&event);
326    return Return<void>();
327}
328
329
330RadioHalHidl::Tuner::Tuner(sp<TunerCallbackInterface> callback, sp<RadioHalHidl> module)
331    : TunerInterface(), mHalTuner(NULL), mCallback(callback), mParentModule(module)
332{
333    // Make sure the handler we are passing in only deals with const members,
334    // as it can be called on an arbitrary thread.
335    const auto& self = this;
336    HalDeathHandler::getInstance()->registerAtExitHandler(
337            this, [&self]() { self->sendHwFailureEvent(); });
338}
339
340
341RadioHalHidl::Tuner::~Tuner()
342{
343    HalDeathHandler::getInstance()->unregisterAtExitHandler(this);
344}
345
346void RadioHalHidl::Tuner::setHalTuner(sp<ITuner>& halTuner) {
347    if (mHalTuner != 0) {
348        mHalTuner->unlinkToDeath(HalDeathHandler::getInstance());
349    }
350    mHalTuner = halTuner;
351    if (mHalTuner != 0) {
352        mHalTuner->linkToDeath(HalDeathHandler::getInstance(), 0 /*cookie*/);
353    }
354}
355
356void RadioHalHidl::Tuner::handleHwFailure()
357{
358    ALOGV("%s IN", __FUNCTION__);
359    sp<RadioHalHidl> parentModule = mParentModule.promote();
360    if (parentModule != 0) {
361        parentModule->clearService();
362    }
363    sendHwFailureEvent();
364    mHalTuner.clear();
365}
366
367void RadioHalHidl::Tuner::sendHwFailureEvent() const
368{
369    radio_hal_event_t event;
370    memset(&event, 0, sizeof(radio_hal_event_t));
371    event.type = RADIO_EVENT_HW_FAILURE;
372    onCallback(&event);
373}
374
375void RadioHalHidl::Tuner::onCallback(radio_hal_event_t *halEvent) const
376{
377    if (mCallback != 0) {
378        mCallback->onEvent(halEvent);
379    }
380}
381
382} // namespace android
383