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

Commit 038b1737 authored by Dake Gu's avatar Dake Gu
Browse files

synchronized audio beat detection: DO NOT MERGE

1. decoder is isolated from aah_decoder_pump to serve both audio playback and
   audio analyzer

2. In transmitter, aah_audio_processor reads audio decoder output,  downsamples
   to 8 bits mono pcm, calculates fft and feeds to aah_audio_algorithm.
   Beat detection is based on selecting significant amplitude change on sub
   bands of frequency domain.  Beat sequence number is increased if there is
   a significant change, all visualizers will switch to same scene based on the
   beat sequence number.

3. A new TRTPMetaDataPacket is added for carrying general metadata information
   from transmitter to receiver.   The packet is sent every 250ms for beat
   information.

4. Audio/Video synchronization: every beat information from transmitter carries
   a media timestamp, receiver performs media time to java elapsedRealtime
   transformation then send to java process.

5. created binder service IAAHMetaDataService, it broadcast beat information to
   all IAAHMetaDataClient.  Modified service_manager.c "allow table" to allow
   mediaserver register IAAHMetaDataService.

6. Added a java static library to access IAAHMetaDataService.  Jni part wraps
   access to native IAAHMetaDataClient.  Robustness: both IAAHMetaDataService
   side and IAAHMetaDataClient sides can detect binder death of the other side,
   and perform cleanup and recovery step for a new connection.

Change-Id: Iaad2a9d9d3aa3990fb796efe59a93bf4efc81b32
parent c43a685c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ static struct {
    { AID_MEDIA, "media.player" },
    { AID_MEDIA, "media.camera" },
    { AID_MEDIA, "media.audio_policy" },
    { AID_MEDIA, "android.media.IAAHMetaDataService" },
    { AID_DRM,   "drm.drmManager" },
    { AID_NFC,   "nfc" },
    { AID_RADIO, "radio.phone" },
+412 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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 "AAHMetaDataServiceJNI"

#include <android_runtime/AndroidRuntime.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <utils/misc.h>

#include "jni.h"
#include "JNIHelp.h"

#include "IAAHMetaData.h"

// error code, synced with MetaDataServiceRtp.java
enum {
    SUCCESS = 0,
    ERROR = -1,
    ALREADY_EXISTS = -2,
};

namespace android {

static const char* kAAHMetaDataServiceBinderName =
        "android.media.IAAHMetaDataService";

static const char* kAAHMetaDataServiceClassName =
        "android/media/libaah/MetaDataServiceRtp";

static struct {
    jmethodID postEventFromNativeId;
    jmethodID flushFromNativeId;
    jfieldID mCookieId;
    jclass clazz;
} jnireflect;

static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
    if (NULL != *array) {
        uint32_t len = env->GetArrayLength(*array);
        if (len >= size)
            return;

        env->DeleteGlobalRef(*array);
        *array = NULL;
    }

    jbyteArray localRef = env->NewByteArray(size);
    if (NULL != localRef) {
        // Promote to global ref.
        *array = (jbyteArray) env->NewGlobalRef(localRef);

        // Release our (now pointless) local ref.
        env->DeleteLocalRef(localRef);
    }
}

// JNIMetaDataService acts as IAAHMetaDataClient, propagates message to java.
// It also starts a background thread that querying and monitoring life cycle
// of IAAHMetaDataService
// JNIMetaDataService will shoot itself when the related java object is garbage
// collected. This might not be important if java program is using singleton
// pattern;  but it's also safe if java program create and destroy the object
// repeatedly.
class JNIMetaDataService : virtual public BnAAHMetaDataClient,
        virtual public android::IBinder::DeathRecipient,
        virtual public Thread {
 public:
    JNIMetaDataService();

    // start working, must be called only once during initialize
    bool start(jobject ref);
    // stop thread and unref this object, you should never access the object
    // after calling destroy()
    void destroy();

    // override BnAAHMetaDataClient
    virtual void notify(uint16_t typeId, uint32_t item_len, const void* data);

    virtual void flush();

    // enable / disable the searching service
    void setEnabled(bool e);

    // override Thread
    virtual bool threadLoop();

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

 private:
    virtual ~JNIMetaDataService();

    sp<JNIMetaDataService> self_strongref;
    sp<JNIMetaDataService> thread_strongref;
    jobject metadataservice_ref;
    jbyteArray metadata_buffer;
    Mutex lock;
    Condition cond;
    volatile bool remote_service_invalid;
    volatile bool exitThread;
    volatile bool enabled;
};

void JNIMetaDataService::notify(uint16_t typeId, uint32_t item_len,
                                const void* data) {
    LOGV("notify received type=%d item_len=%d", typeId, item_len);
    if (!enabled) {
        return;
    }
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    // ensureArraySize provides some simple optimization of reusing
    // the byte array object.  If in the future that different types
    // of metadata hit client, then more sophisticated strategy is needed.
    ensureArraySize(env, &metadata_buffer, item_len);
    if (metadata_buffer) {
        jbyte *nArray = env->GetByteArrayElements(metadata_buffer, NULL);
        memcpy(nArray, data, item_len);
        env->ReleaseByteArrayElements(metadata_buffer, nArray, 0);
    }
    env->CallStaticVoidMethod(jnireflect.clazz,
                              jnireflect.postEventFromNativeId,
                              metadataservice_ref, typeId, item_len,
                              metadata_buffer);
}

void JNIMetaDataService::flush() {
    if (!enabled) {
        return;
    }
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    env->CallStaticVoidMethod(jnireflect.clazz,
                              jnireflect.flushFromNativeId,
                              metadataservice_ref);
}

JNIMetaDataService::JNIMetaDataService()
        : metadataservice_ref(NULL),
          metadata_buffer(NULL),
          remote_service_invalid(true),
          exitThread(false),
          enabled(false) {
    // Holds strong reference to myself, because the way that binder works
    // requires to use RefBase,  we cannot explicitly delete this object,
    // otherwise, access from service manager might cause segfault.
    // So we hold this reference until destroy() is called.
    // Alternative solution is to create another JNIMetaDataServiceCookie class
    // which holds the strong reference but that adds more memory fragmentation
    self_strongref = this;
}

JNIMetaDataService::~JNIMetaDataService() {
    LOGV("~JNIMetaDataService");
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (metadata_buffer) {
        env->DeleteGlobalRef(metadata_buffer);
        metadata_buffer = NULL;
    }
    if (metadataservice_ref) {
        env->DeleteGlobalRef(metadataservice_ref);
        metadataservice_ref = NULL;
    }
}

bool JNIMetaDataService::threadLoop() {
    LOGV("Enter JNIMetaDataService::threadLoop");
    sp < IServiceManager > sm = defaultServiceManager();
    sp < IBinder > binder;
    sp<IAAHMetaDataService> remote_service;
    lock.lock();
    while (true) {
        if (exitThread) {
            break;
        } else if (remote_service_invalid && enabled) {
            // getService() may block 10s, so we do this not holding lock
            lock.unlock();
            binder = sm->getService(
                    String16(kAAHMetaDataServiceBinderName));
            lock.lock();
            if (binder != NULL) {
                LOGD("found remote %s", kAAHMetaDataServiceBinderName);
                if (remote_service.get()) {
                    remote_service->asBinder()->unlinkToDeath(this);
                    remote_service->removeClient(thread_strongref);
                    remote_service = NULL;
                }
                remote_service = interface_cast < IAAHMetaDataService
                        > (binder);
                remote_service->asBinder()->linkToDeath(this);
                remote_service->addClient(thread_strongref);
                remote_service_invalid = false;
            }
        }
        if (!exitThread && !(remote_service_invalid && enabled)) {
            // if exitThread flag is not set and we are not searching remote
            // service, wait next signal to be triggered either
            //   - destroy() being called
            //   - enabled or remote_service_invalid changed
            cond.wait(lock);
        }
    }
    if (remote_service.get()) {
        remote_service->removeClient(thread_strongref);
        remote_service->asBinder()->unlinkToDeath(this);
        remote_service = NULL;
    }
    lock.unlock();
    binder = NULL;
    sm = NULL;
    // cleanup the thread reference
    thread_strongref = NULL;
    LOGV("Exit JNIMetaDataService::threadLoop");
    return false;
}

bool JNIMetaDataService::start(jobject ref) {
    metadataservice_ref = ref;
    // now add a strong ref, used in threadLoop()
    thread_strongref = this;
    if (NO_ERROR
            != run("aah_metadataservice_monitor", ANDROID_PRIORITY_NORMAL)) {
        thread_strongref = NULL;
        return false;
    }
    return true;
}

void JNIMetaDataService::destroy() {
    lock.lock();
    exitThread = true;
    lock.unlock();
    cond.signal();
    // unref JNIMetaDataService, JNIMetaDataService will not be deleted for now;
    // it will be deleted when thread exits and cleans thread_strongref.
    self_strongref = NULL;
}

void JNIMetaDataService::setEnabled(bool e) {
    bool sendSignal;
    lock.lock();
    sendSignal = e && !enabled;
    enabled = e;
    lock.unlock();
    if (sendSignal) {
        cond.signal();
    }
}

void JNIMetaDataService::binderDied(const wp<IBinder>& who) {
    LOGD("remote %s died, re-searching...", kAAHMetaDataServiceBinderName);
    bool sendSignal;
    lock.lock();
    remote_service_invalid = true;
    sendSignal = enabled;
    lock.unlock();
    if (sendSignal) {
        cond.signal();
    }
}

// called by java object to initialize the native part
static jint aahmetadataservice_native_setup(JNIEnv* env, jobject thiz,
                                            jobject weak_this) {

    jint retvalue = SUCCESS;
    jobject ref;
    JNIMetaDataService* lpJniService = new JNIMetaDataService();
    if (lpJniService == NULL) {
        LOGE("setup: Error in allocating JNIMetaDataService");
        retvalue = ERROR;
        goto setup_failure;
    }

    // we use a weak reference so the java object can be garbage collected.
    ref = env->NewGlobalRef(weak_this);
    if (ref == NULL) {
        LOGE("setup: Error in NewGlobalRef");
        retvalue = ERROR;
        goto setup_failure;
    }

    LOGV("setup: lpJniService: %p metadataservice_ref %p", lpJniService, ref);

    env->SetIntField(thiz, jnireflect.mCookieId,
                     reinterpret_cast<jint>(lpJniService));

    if (!lpJniService->start(ref)) {
        retvalue = ERROR;
        LOGE("setup: Error in starting JNIMetaDataService");
        goto setup_failure;
    }

    return retvalue;

    // failures:
    setup_failure:

    if (lpJniService != NULL) {
        lpJniService->destroy();
    }

    return retvalue;
}

inline JNIMetaDataService* get_service(JNIEnv* env, jobject thiz) {
    return reinterpret_cast<JNIMetaDataService*>(env->GetIntField(
            thiz, jnireflect.mCookieId));
}

// called when the java object is garbaged collected
static void aahmetadataservice_native_finalize(JNIEnv* env, jobject thiz) {
    JNIMetaDataService* pService = get_service(env, thiz);
    if (pService == NULL) {
        return;
    }
    LOGV("finalize jni object");
    // clean up the service object
    pService->destroy();
    env->SetIntField(thiz, jnireflect.mCookieId, 0);
}

static void aahmetadataservice_native_enable(JNIEnv* env, jobject thiz) {
    JNIMetaDataService* pService = get_service(env, thiz);
    if (pService == NULL) {
        LOGD("native service already deleted");
        return;
    }
    pService->setEnabled(true);
}

static void aahmetadataservice_native_disable(JNIEnv* env, jobject thiz) {
    JNIMetaDataService* pService = get_service(env, thiz);
    if (pService == NULL) {
        LOGD("native service already deleted");
        return;
    }
    pService->setEnabled(false);
}

static JNINativeMethod kAAHMetaDataServiceMethods[] = {
    { "native_setup", "(Ljava/lang/Object;)I",
            (void *) aahmetadataservice_native_setup },
    { "native_enable", "()V", (void *) aahmetadataservice_native_enable },
    { "native_disable", "()V", (void *) aahmetadataservice_native_disable },
    { "native_finalize", "()V", (void *) aahmetadataservice_native_finalize },
};

static jint jniOnLoad(JavaVM* vm, void* reserved) {
    LOGV("jniOnLoad");
    JNIEnv* env = NULL;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        return -1;
    }

    jclass clazz = env->FindClass(kAAHMetaDataServiceClassName);
    if (!clazz) {
        LOGE("ERROR: FindClass failed\n");
        return -1;
    }

    jnireflect.clazz = (jclass) env->NewGlobalRef(clazz);

    if (env->RegisterNatives(jnireflect.clazz, kAAHMetaDataServiceMethods,
                             NELEM(kAAHMetaDataServiceMethods)) < 0) {
        LOGE("ERROR: RegisterNatives failed\n");
        return -1;
    }

    jnireflect.postEventFromNativeId = env->GetStaticMethodID(
            jnireflect.clazz, "postMetaDataFromNative",
            "(Ljava/lang/Object;SI[B)V");
    if (!jnireflect.postEventFromNativeId) {
        LOGE("Can't find %s", "postMetaDataFromNative");
        return -1;
    }
    jnireflect.flushFromNativeId = env->GetStaticMethodID(
            jnireflect.clazz, "flushFromNative",
            "(Ljava/lang/Object;)V");
    if (!jnireflect.flushFromNativeId) {
        LOGE("Can't find %s", "flushFromNative");
        return -1;
    }

    jnireflect.mCookieId = env->GetFieldID(jnireflect.clazz, "mCookie", "I");
    if (!jnireflect.mCookieId) {
        LOGE("Can't find %s", "mCookie");
        return -1;
    }

    return JNI_VERSION_1_4;
}

}

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    return android::jniOnLoad(vm, reserved);
}
+6 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@ LOCAL_MODULE := libaah_rtp
LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := \
    aah_audio_algorithm.cpp \
    aah_audio_processor.cpp \
    aah_decoder_pump.cpp \
    aah_rx_player.cpp \
    aah_rx_player_core.cpp \
@@ -17,6 +19,8 @@ LOCAL_SRC_FILES := \
    aah_tx_group.cpp \
    aah_tx_packet.cpp \
    aah_tx_player.cpp \
    AAHMetaDataService_jni.cpp \
    IAAHMetaData.cpp \
    utils.cpp

LOCAL_C_INCLUDES := \
@@ -26,6 +30,7 @@ LOCAL_C_INCLUDES := \
    frameworks/base/media/libstagefright

LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libbinder \
    libcommon_time_client \
    libcutils \
@@ -39,3 +44,4 @@ LOCAL_LDLIBS := \

include $(BUILD_SHARED_LIBRARY)

include $(call all-makefiles-under,$(LOCAL_PATH))
+222 −0
Original line number Diff line number Diff line
/*
 **
 ** Copyright 2012, 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 "LibAAH_RTP"
//#define LOG_NDEBUG 0
#include <utils/Log.h>

#include <binder/IServiceManager.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <media/stagefright/Utils.h>

#include <time.h>

#include "IAAHMetaData.h"

namespace android {

enum {
    NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
    FLUSH = IBinder::FIRST_CALL_TRANSACTION + 1,
};

class BpAAHMetaDataClient : public BpInterface<IAAHMetaDataClient> {
 public:
    BpAAHMetaDataClient(const sp<IBinder>& impl)
            : BpInterface<IAAHMetaDataClient>(impl) {
    }

    virtual void notify(uint16_t typeId, uint32_t item_len, const void* buf) {
        Parcel data, reply;
        data.writeInterfaceToken(IAAHMetaDataClient::getInterfaceDescriptor());
        data.writeInt32((int32_t) typeId);
        data.writeInt32((int32_t) item_len);
        data.write(buf, item_len);
        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
    }
    virtual void flush() {
        Parcel data, reply;
        data.writeInterfaceToken(IAAHMetaDataClient::getInterfaceDescriptor());
        remote()->transact(FLUSH, data, &reply, IBinder::FLAG_ONEWAY);
    }
};

IMPLEMENT_META_INTERFACE(AAHMetaDataClient, "android.media.IAAHMetaDataClient");

// ----------------------------------------------------------------------

status_t BnAAHMetaDataClient::onTransact(uint32_t code, const Parcel& data,
                                         Parcel* reply, uint32_t flags) {
    switch (code) {
        case NOTIFY: {
            CHECK_INTERFACE(IAAHMetaDataClient, data, reply);
            uint16_t typeId = (uint16_t) data.readInt32();
            uint32_t item_len = (uint32_t) data.readInt32();
            const void* buf = data.readInplace(item_len);

            notify(typeId, item_len, buf);
            return NO_ERROR;
        }
            break;
        case FLUSH: {
            CHECK_INTERFACE(IAAHMetaDataClient, data, reply);
            flush();
            return NO_ERROR;
        }
            break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

enum {
    ADDCLIENT = IBinder::FIRST_CALL_TRANSACTION,
    REMOVECLIENT = ADDCLIENT + 1,
};

class BpAAHMetaDataService : public BpInterface<IAAHMetaDataService> {
 public:
    BpAAHMetaDataService(const sp<IBinder>& impl)
            : BpInterface<IAAHMetaDataService>(impl) {
    }

    virtual void addClient(const sp<IAAHMetaDataClient>& client) {
        Parcel data, reply;
        data.writeInterfaceToken(IAAHMetaDataService::getInterfaceDescriptor());
        data.writeStrongBinder(client->asBinder());
        remote()->transact(ADDCLIENT, data, &reply, IBinder::FLAG_ONEWAY);
    }

    virtual void removeClient(const sp<IAAHMetaDataClient>& client) {
        Parcel data, reply;
        data.writeInterfaceToken(IAAHMetaDataService::getInterfaceDescriptor());
        data.writeStrongBinder(client->asBinder());
        remote()->transact(REMOVECLIENT, data, &reply, IBinder::FLAG_ONEWAY);
    }
};

IMPLEMENT_META_INTERFACE(AAHMetaDataService, "android.media.IAAHMetaDataService");

// ----------------------------------------------------------------------

status_t BnAAHMetaDataService::onTransact(uint32_t code, const Parcel& data,
                                          Parcel* reply, uint32_t flags) {
    switch (code) {
        case ADDCLIENT: {
            CHECK_INTERFACE(IAAHMetaDataService, data, reply);
            sp<IAAHMetaDataClient> client = interface_cast < IAAHMetaDataClient
                    > (data.readStrongBinder());

            addClient(client);
            return NO_ERROR;
        }
            break;
        case REMOVECLIENT: {
            CHECK_INTERFACE(IAAHMetaDataService, data, reply);
            sp<IAAHMetaDataClient> client = interface_cast < IAAHMetaDataClient
                    > (data.readStrongBinder());

            removeClient(client);
            return NO_ERROR;
        }
            break;
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

static bool s_aah_metadata_service_initialized = false;
static sp<AAHMetaDataService> s_aah_metadata_service = NULL;
static Mutex s_aah_metadata_service_lock;

const sp<AAHMetaDataService>& AAHMetaDataService::getInstance() {
    Mutex::Autolock autolock(&s_aah_metadata_service_lock);
    if (!s_aah_metadata_service_initialized) {
        s_aah_metadata_service = new AAHMetaDataService();
        status_t ret = android::defaultServiceManager()->addService(
                IAAHMetaDataService::descriptor, s_aah_metadata_service);
        if (ret != 0) {
            LOGE("failed to add AAHMetaDataService error code %d", ret);
            s_aah_metadata_service = NULL;
        }
        s_aah_metadata_service_initialized = true;
    }
    return s_aah_metadata_service;
}

AAHMetaDataService::AAHMetaDataService() {
}

void AAHMetaDataService::addClient(const sp<IAAHMetaDataClient>& client) {
    Mutex::Autolock lock(mLock);
    IAAHMetaDataClient* obj = client.get();
    LOGV("addClient %p", obj);
    client->asBinder()->linkToDeath(this);
    mClients.add(client);
}

void AAHMetaDataService::binderDied(const wp<IBinder>& who) {
    Mutex::Autolock lock(mLock);
    for (uint32_t i = 0; i < mClients.size(); ++i) {
        const sp<IAAHMetaDataClient>& c = mClients[i];
        if (who == c->asBinder()) {
            LOGD("IAAHMetaDataClient binder Died");
            LOGV("removed died client %p", c.get());
            mClients.removeAt(i);
            return;
        }
    }
}

void AAHMetaDataService::removeClient(const sp<IAAHMetaDataClient>& client) {
    IAAHMetaDataClient* obj = client.get();
    Mutex::Autolock lock(mLock);
    for (uint32_t i = 0; i < mClients.size(); ++i) {
        const sp<IAAHMetaDataClient>& c = mClients[i];
        if (c->asBinder() == client->asBinder()) {
            LOGV("removeClient %p", c.get());
            mClients.removeAt(i);
            return;
        }
    }
}

void AAHMetaDataService::broadcast(uint16_t typeId, uint32_t item_len,
                                   void* data) {
    LOGV("broadcast %d", typeId);
    Mutex::Autolock lock(mLock);
    uint8_t* buf = reinterpret_cast<uint8_t*>(data);
    for (uint32_t i = 0; i < mClients.size(); ++i) {
        const sp<IAAHMetaDataClient> c = mClients[i];
        LOGV("notify %p", c.get());
        c->notify(typeId, item_len, data);
    }
}

void AAHMetaDataService::flush() {
    Mutex::Autolock lock(mLock);
    for (uint32_t i = 0; i < mClients.size(); ++i) {
        const sp<IAAHMetaDataClient> c = mClients[i];
        c->flush();
    }
}
}
;
// namespace android
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2012 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_IAAHMETADATA_H
#define ANDROID_IAAHMETADATA_H

#include <utils/SortedVector.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>

#include "utils.h"

namespace android {

class IAAHMetaDataClient : public IInterface {
 public:
    DECLARE_META_INTERFACE (AAHMetaDataClient);

    virtual void notify(uint16_t typeId, uint32_t item_len,
                        const void* data) = 0;
    virtual void flush() = 0;
};

// ----------------------------------------------------------------------------

class BnAAHMetaDataClient : public BnInterface<IAAHMetaDataClient> {
 public:
    virtual status_t onTransact(uint32_t code, const Parcel& data,
                                Parcel* reply, uint32_t flags = 0);
};

// ----------------------------------------------------------------------------

class IAAHMetaDataService : public IInterface {
 public:
    DECLARE_META_INTERFACE (AAHMetaDataService);

    virtual void addClient(const sp<IAAHMetaDataClient>& client) = 0;
    virtual void removeClient(const sp<IAAHMetaDataClient>& client) = 0;
};

// ----------------------------------------------------------------------------

class BnAAHMetaDataService : public BnInterface<IAAHMetaDataService> {
 public:
    virtual status_t onTransact(uint32_t code, const Parcel& data,
                                Parcel* reply, uint32_t flags = 0);
};

// ----------------------------------------------------------------------------

class AAHMetaDataService : public BnAAHMetaDataService,
        public android::IBinder::DeathRecipient {
 public:
    static const sp<AAHMetaDataService>& getInstance();
    void broadcast(uint16_t typeId, uint32_t item_len, void* data);
    void flush();
    virtual void addClient(const sp<IAAHMetaDataClient>& client);
    virtual void removeClient(const sp<IAAHMetaDataClient>& client);
    virtual void binderDied(const wp<IBinder>& who);
 private:
    AAHMetaDataService();

    SortedVector<sp<IAAHMetaDataClient> > mClients;
    Mutex mLock;

};

}
;
// namespace android

#endif // ANDROID_IAAHMETADATA_H
Loading