Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0125d057 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "radio client"

parents b31fe157 dedc86dc
Loading
Loading
Loading
Loading

include/radio/Radio.h

0 → 100644
+88 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#ifndef ANDROID_HARDWARE_RADIO_H
#define ANDROID_HARDWARE_RADIO_H

#include <binder/IBinder.h>
#include <utils/threads.h>
#include <radio/RadioCallback.h>
#include <radio/IRadio.h>
#include <radio/IRadioService.h>
#include <radio/IRadioClient.h>
#include <system/radio.h>

namespace android {

class MemoryDealer;

class Radio : public BnRadioClient,
                        public IBinder::DeathRecipient
{
public:

    virtual ~Radio();

    static  status_t listModules(struct radio_properties *properties,
                                 uint32_t *numModules);
    static  sp<Radio> attach(radio_handle_t handle,
                             const struct radio_band_config *config,
                             bool withAudio,
                             const sp<RadioCallback>& callback);


            void detach();

            status_t setConfiguration(const struct radio_band_config *config);

            status_t getConfiguration(struct radio_band_config *config);

            status_t setMute(bool mute);

            status_t getMute(bool *mute);

            status_t step(radio_direction_t direction, bool skipSubChannel);

            status_t scan(radio_direction_t direction, bool skipSubChannel);

            status_t tune(unsigned int channel, unsigned int subChannel);

            status_t cancel();

            status_t getProgramInformation(struct radio_program_info *info);

            status_t hasControl(bool *hasControl);

            // BpRadioClient
            virtual void onEvent(const sp<IMemory>& eventMemory);

            //IBinder::DeathRecipient
            virtual void binderDied(const wp<IBinder>& who);

private:
            Radio(radio_handle_t handle,
                            const sp<RadioCallback>&);
            static const sp<IRadioService>& getRadioService();

            Mutex                   mLock;
            sp<IRadio>              mIRadio;
            const radio_handle_t    mHandle;
            sp<RadioCallback>       mCallback;
};

}; // namespace android

#endif //ANDROID_HARDWARE_RADIO_H
+38 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#ifndef ANDROID_HARDWARE_RADIO_CALLBACK_H
#define ANDROID_HARDWARE_RADIO_CALLBACK_H

#include <utils/RefBase.h>
#include <system/radio.h>

namespace android {

class RadioCallback : public RefBase
{
public:

            RadioCallback() {}
    virtual ~RadioCallback() {}

    virtual void onEvent(struct radio_event *event) = 0;

};

}; // namespace android

#endif //ANDROID_HARDWARE_RADIO_CALLBACK_H
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
	Radio.cpp \
	IRadio.cpp \
	IRadioClient.cpp \
	IRadioService.cpp

radio/Radio.cpp

0 → 100644
+283 −0
Original line number Diff line number Diff line
/*
**
** 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