/*
 * 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_SERVICE_H
#define ANDROID_HARDWARE_RADIO_SERVICE_H

#include <utils/Vector.h>
//#include <binder/AppOpsManager.h>
#include <binder/MemoryDealer.h>
#include <binder/BinderService.h>
#include <binder/IAppOpsCallback.h>
#include <radio/IRadioService.h>
#include <radio/IRadio.h>
#include <radio/IRadioClient.h>
#include <system/radio.h>
#include <hardware/radio.h>

namespace android {

class MemoryHeapBase;

class RadioService :
    public BinderService<RadioService>,
    public BnRadioService
{
    friend class BinderService<RadioService>;

public:
    class ModuleClient;
    class Module;

    static char const* getServiceName() { return "media.radio"; }

                        RadioService();
    virtual             ~RadioService();

    // IRadioService
    virtual status_t listModules(struct radio_properties *properties,
                                 uint32_t *numModules);

    virtual status_t attach(radio_handle_t handle,
                            const sp<IRadioClient>& client,
                            const struct radio_band_config *config,
                            bool withAudio,
                            sp<IRadio>& radio);

    virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                   Parcel* reply, uint32_t flags);

    virtual status_t    dump(int fd, const Vector<String16>& args);


    class Module : public virtual RefBase {
    public:

       Module(radio_hw_device* hwDevice,
              struct radio_properties properties);

       virtual ~Module();

               sp<ModuleClient> addClient(const sp<IRadioClient>& client,
                                  const struct radio_band_config *config,
                                  bool audio);

               void removeClient(const sp<ModuleClient>& moduleClient);

               status_t setMute(bool mute);

               status_t getMute(bool *mute);

       virtual status_t dump(int fd, const Vector<String16>& args);

       const struct radio_hw_device *hwDevice() const { return mHwDevice; }
       const struct radio_properties properties() const { return mProperties; }
       const struct radio_band_config *getDefaultConfig() const ;

    private:

       void notifyDeviceConnection(bool connected, const char *address);

        Mutex                         mLock;          // protects  mModuleClients
        const struct radio_hw_device  *mHwDevice;     // HAL hardware device
        const struct radio_properties mProperties;    // cached hardware module properties
        Vector< sp<ModuleClient> >    mModuleClients; // list of attached clients
        bool                          mMute;          // radio audio source state
                                                      // when unmuted, audio is routed to the
                                                      // output device selected for media use case.
    }; // class Module

    class CallbackThread : public Thread {
    public:

        CallbackThread(const wp<ModuleClient>& moduleClient);

        virtual ~CallbackThread();


        // Thread virtuals
        virtual bool threadLoop();

        // RefBase
        virtual void onFirstRef();

                void exit();

                void sendEvent(radio_hal_event_t *halEvent);
                sp<IMemory> prepareEvent(radio_hal_event_t *halEvent);

    private:
        wp<ModuleClient>      mModuleClient;    // client module the thread belongs to
        Condition             mCallbackCond;    // condition signaled when a new event is posted
        Mutex                 mCallbackLock;    // protects mEventQueue
        Vector< sp<IMemory> > mEventQueue;      // pending callback events
        sp<MemoryDealer>      mMemoryDealer;    // shared memory for callback event
    }; // class CallbackThread

    class ModuleClient : public BnRadio,
                   public IBinder::DeathRecipient {
    public:

       ModuleClient(const sp<Module>& module,
              const sp<IRadioClient>& client,
              const struct radio_band_config *config,
              bool audio);

       virtual ~ModuleClient();

       // IRadio
       virtual void detach();

       virtual status_t setConfiguration(const struct radio_band_config *config);

       virtual status_t getConfiguration(struct radio_band_config *config);

       virtual status_t setMute(bool mute);

       virtual status_t getMute(bool *mute);

       virtual status_t scan(radio_direction_t direction, bool skipSubChannel);

       virtual status_t step(radio_direction_t direction, bool skipSubChannel);

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

       virtual status_t cancel();

       virtual status_t getProgramInformation(struct radio_program_info *info);

       virtual status_t hasControl(bool *hasControl);

       virtual status_t dump(int fd, const Vector<String16>& args);

               sp<IRadioClient> client() const { return mClient; }
               wp<Module> module() const { return mModule; }
               radio_hal_band_config_t halConfig() const;
               sp<CallbackThread> callbackThread() const { return mCallbackThread; }
               void setTuner(const struct radio_tuner *tuner);
               const struct radio_tuner *getTuner() const;
               bool audio() const { return mAudio; }

               void onCallbackEvent(const sp<IMemory>& event);

       virtual void onFirstRef();


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

    private:

        mutable Mutex               mLock;           // protects mClient, mConfig and mTuner
        wp<Module>                  mModule;         // The module this client is attached to
        sp<IRadioClient>            mClient;         // event callback binder interface
        radio_band_config_t         mConfig;         // current band configuration
        sp<CallbackThread>          mCallbackThread; // event callback thread
        const bool                  mAudio;
        const struct radio_tuner    *mTuner;        // HAL tuner interface. NULL indicates that
                                                    // this client does not have control on any
                                                    // tuner
    }; // class ModuleClient


    static void callback(radio_hal_event_t *halEvent, void *cookie);

private:

    virtual void onFirstRef();

    static void convertProperties(radio_properties_t *properties,
                                  const radio_hal_properties_t *halProperties);
    Mutex               mServiceLock;   // protects mModules
    volatile int32_t    mNextUniqueId;  // for module ID allocation
    DefaultKeyedVector< radio_handle_t, sp<Module> > mModules;
};

} // namespace android

#endif // ANDROID_HARDWARE_RADIO_SERVICE_H