Merge "stagefright: don't update meta for the same buffer"
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3479f76..a8dcbe0 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -44,6 +44,7 @@
         kStreamedFromLocalHost = 2,
         kIsCachingDataSource   = 4,
         kIsHTTPBasedSource     = 8,
+        kIsLocalFileSource     = 16,
     };
 
     static sp<DataSource> CreateFromURI(
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
index b6349e0..9f3bb5e 100644
--- a/include/media/stagefright/FileSource.h
+++ b/include/media/stagefright/FileSource.h
@@ -39,6 +39,10 @@
 
     virtual status_t getSize(off64_t *size);
 
+    virtual uint32_t flags() {
+        return kIsLocalFileSource;
+    }
+
     virtual sp<DecryptHandle> DrmInitialization(const char *mime);
 
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index 93e9a4b..ef55620 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -89,6 +89,8 @@
     // Add a SynPoint derived from |event|.
     void addSyncPoint_l(const ATSParser::SyncEvent &event);
 
+    status_t  estimateDurationsFromTimesUsAtEnd();
+
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
 };
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 844479e..4975d9a 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -90,6 +90,10 @@
         return mParser->mFlags;
     }
 
+    uint64_t firstPTS() const {
+        return mFirstPTS;
+    }
+
 private:
     struct StreamInfo {
         unsigned mType;
@@ -135,6 +139,7 @@
 
     void signalEOS(status_t finalResult);
 
+    SourceType getSourceType();
     sp<MediaSource> getSource(SourceType type);
 
     bool isAudio() const;
@@ -208,11 +213,12 @@
     : mHasReturnedData(false), mOffset(offset), mTimeUs(0) {}
 
 void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
-        int64_t timeUs) {
+        int64_t timeUs, SourceType type) {
     mHasReturnedData = true;
     mOffset = offset;
     mMediaSource = source;
     mTimeUs = timeUs;
+    mType = type;
 }
 
 void ATSParser::SyncEvent::reset() {
@@ -1121,13 +1127,24 @@
                 int64_t timeUs;
                 if (accessUnit->meta()->findInt64("timeUs", &timeUs)) {
                     found = true;
-                    event->init(pesStartOffset, mSource, timeUs);
+                    event->init(pesStartOffset, mSource, timeUs, getSourceType());
                 }
             }
         }
     }
 }
 
+ATSParser::SourceType ATSParser::Stream::getSourceType() {
+    if (isVideo()) {
+        return VIDEO;
+    } else if (isAudio()) {
+        return AUDIO;
+    } else if (isMeta()) {
+        return META;
+    }
+    return NUM_SOURCE_TYPES;
+}
+
 sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
     switch (type) {
         case VIDEO:
@@ -1565,6 +1582,16 @@
     return mPrograms.editItemAt(0)->PTSTimeDeltaEstablished();
 }
 
+int64_t ATSParser::getFirstPTSTimeUs() {
+    for (size_t i = 0; i < mPrograms.size(); ++i) {
+        sp<ATSParser::Program> program = mPrograms.itemAt(i);
+        if (program->PTSTimeDeltaEstablished()) {
+            return (program->firstPTS() * 100) / 9;
+        }
+    }
+    return -1;
+}
+
 __attribute__((no_sanitize("integer")))
 void ATSParser::updatePCR(
         unsigned /* PID */, uint64_t PCR, uint64_t byteOffsetFromStart) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 2b166f0..faae6c9 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -62,18 +62,26 @@
         ALIGNED_VIDEO_DATA         = 2,
     };
 
+    enum SourceType {
+        VIDEO = 0,
+        AUDIO = 1,
+        META  = 2,
+        NUM_SOURCE_TYPES = 3
+    };
+
     // Event is used to signal sync point event at feedTSPacket().
     struct SyncEvent {
         explicit SyncEvent(off64_t offset);
 
         void init(off64_t offset, const sp<MediaSource> &source,
-                int64_t timeUs);
+                int64_t timeUs, SourceType type);
 
         bool hasReturnedData() const { return mHasReturnedData; }
         void reset();
         off64_t getOffset() const { return mOffset; }
         const sp<MediaSource> &getMediaSource() const { return mMediaSource; }
         int64_t getTimeUs() const { return mTimeUs; }
+        SourceType getType() const { return mType; }
 
     private:
         bool mHasReturnedData;
@@ -87,6 +95,7 @@
         sp<MediaSource> mMediaSource;
         /* The timestamp of the sync frame. */
         int64_t mTimeUs;
+        SourceType mType;
     };
 
     explicit ATSParser(uint32_t flags = 0);
@@ -107,17 +116,13 @@
 
     void signalEOS(status_t finalResult);
 
-    enum SourceType {
-        VIDEO = 0,
-        AUDIO = 1,
-        META  = 2,
-        NUM_SOURCE_TYPES = 3
-    };
     sp<MediaSource> getSource(SourceType type);
     bool hasSource(SourceType type) const;
 
     bool PTSTimeDeltaEstablished();
 
+    int64_t getFirstPTSTimeUs();
+
     enum {
         // From ISO/IEC 13818-1: 2000 (E), Table 2-29
         STREAMTYPE_RESERVED             = 0x00,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 4fcf7b5..548f44e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -44,6 +44,7 @@
       mEnabled(true),
       mFormat(NULL),
       mLastQueuedTimeUs(0),
+      mEstimatedBufferDurationUs(-1),
       mEOSResult(OK),
       mLatestEnqueuedMeta(NULL),
       mLatestDequeuedMeta(NULL) {
@@ -309,6 +310,8 @@
 
     mFormat = NULL;
     mLatestEnqueuedMeta = NULL;
+
+    mEstimatedBufferDurationUs = -1;
 }
 
 void AnotherPacketSource::queueDiscontinuity(
@@ -431,6 +434,31 @@
     return durationUs;
 }
 
+int64_t AnotherPacketSource::getEstimatedBufferDurationUs() {
+    Mutex::Autolock autoLock(mLock);
+    if (mEstimatedBufferDurationUs >= 0) {
+        return mEstimatedBufferDurationUs;
+    }
+
+    SortedVector<int64_t> maxTimesUs;
+    List<sp<ABuffer> >::iterator it;
+    int64_t t1 = 0, t2 = 0;
+    for (it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+        int64_t timeUs = 0;
+        const sp<ABuffer> &buffer = *it;
+        if (!buffer->meta()->findInt64("timeUs", &timeUs)) {
+            continue;
+        }
+        maxTimesUs.add(timeUs);
+        while (maxTimesUs.size() > 2) {
+            maxTimesUs.removeAt(0);
+            t1 = maxTimesUs.itemAt(0);
+            t2 = maxTimesUs.itemAt(1);
+        }
+    }
+    return mEstimatedBufferDurationUs = t2 - t1;
+}
+
 status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
     *timeUs = 0;
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index dd6849e..b0890d7 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -57,6 +57,9 @@
     // presentation timestamps since the last discontinuity (if any).
     int64_t getBufferedDurationUs(status_t *finalResult);
 
+    // Returns the difference between the two largest timestamps queued
+    int64_t getEstimatedBufferDurationUs();
+
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -113,6 +116,7 @@
     bool mEnabled;
     sp<MetaData> mFormat;
     int64_t mLastQueuedTimeUs;
+    int64_t mEstimatedBufferDurationUs;
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
     sp<AMessage> mLatestEnqueuedMeta;
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 116a5bc..bde33dc 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -40,6 +41,8 @@
 namespace android {
 
 static const size_t kTSPacketSize = 188;
+static const int kMaxDurationReadSize = 250000LL;
+static const int kMaxDurationRetry = 6;
 
 struct MPEG2TSSource : public MediaSource {
     MPEG2TSSource(
@@ -243,6 +246,8 @@
             const sp<MetaData> meta = impl->getFormat();
             meta->setInt64(kKeyDuration, durationUs);
             impl->setFormat(meta);
+        } else {
+            estimateDurationsFromTimesUsAtEnd();
         }
     }
 
@@ -301,6 +306,106 @@
     }
 }
 
+status_t MPEG2TSExtractor::estimateDurationsFromTimesUsAtEnd()  {
+    if (!(mDataSource->flags() & DataSource::kIsLocalFileSource)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    off64_t size = 0;
+    status_t err = mDataSource->getSize(&size);
+    if (err != OK) {
+        return err;
+    }
+
+    uint8_t packet[kTSPacketSize];
+    const off64_t zero = 0;
+    off64_t offset = max(zero, size - kMaxDurationReadSize);
+    if (mDataSource->readAt(offset, &packet, 0) < 0) {
+        return ERROR_IO;
+    }
+
+    int retry = 0;
+    bool allDurationsFound = false;
+    int64_t timeAnchorUs = mParser->getFirstPTSTimeUs();
+    do {
+        int bytesRead = 0;
+        sp<ATSParser> parser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
+        ATSParser::SyncEvent ev(0);
+        offset = max(zero, size - (kMaxDurationReadSize << retry));
+        offset = (offset / kTSPacketSize) * kTSPacketSize;
+        for (;;) {
+            if (bytesRead >= kMaxDurationReadSize << max(0, retry - 1)) {
+                break;
+            }
+
+            ssize_t n = mDataSource->readAt(offset, packet, kTSPacketSize);
+            if (n < 0) {
+                return n;
+            } else if (n < (ssize_t)kTSPacketSize) {
+                break;
+            }
+
+            offset += kTSPacketSize;
+            bytesRead += kTSPacketSize;
+            err = parser->feedTSPacket(packet, kTSPacketSize, &ev);
+            if (err != OK) {
+                return err;
+            }
+
+            if (ev.hasReturnedData()) {
+                int64_t durationUs = ev.getTimeUs();
+                ATSParser::SourceType type = ev.getType();
+                ev.reset();
+
+                int64_t firstTimeUs;
+                sp<AnotherPacketSource> src =
+                    (AnotherPacketSource *)mParser->getSource(type).get();
+                if (src == NULL || src->nextBufferTime(&firstTimeUs) != OK) {
+                    continue;
+                }
+                durationUs += src->getEstimatedBufferDurationUs();
+                durationUs -= timeAnchorUs;
+                durationUs -= firstTimeUs;
+                if (durationUs > 0) {
+                    int64_t origDurationUs, lastDurationUs;
+                    const sp<MetaData> meta = src->getFormat();
+                    const uint32_t kKeyLastDuration = 'ldur';
+                    // Require two consecutive duration calculations to be within 1 sec before
+                    // updating; use MetaData to store previous duration estimate in per-stream
+                    // context.
+                    if (!meta->findInt64(kKeyDuration, &origDurationUs)
+                            || !meta->findInt64(kKeyLastDuration, &lastDurationUs)
+                            || (origDurationUs < durationUs
+                             && abs(durationUs - lastDurationUs) < 60000000)) {
+                        meta->setInt64(kKeyDuration, durationUs);
+                    }
+                    meta->setInt64(kKeyLastDuration, durationUs);
+                }
+            }
+        }
+
+        if (!allDurationsFound) {
+            allDurationsFound = true;
+            for (auto t: {ATSParser::VIDEO, ATSParser::AUDIO}) {
+                sp<AnotherPacketSource> src = (AnotherPacketSource *)mParser->getSource(t).get();
+                if (src == NULL) {
+                    continue;
+                }
+                int64_t durationUs;
+                const sp<MetaData> meta = src->getFormat();
+                if (!meta->findInt64(kKeyDuration, &durationUs)) {
+                    allDurationsFound = false;
+                    break;
+                }
+            }
+        }
+
+        ++retry;
+    } while(!allDurationsFound && offset > 0 && retry <= kMaxDurationRetry);
+
+    return allDurationsFound? OK : ERROR_UNSUPPORTED;
+}
+
 uint32_t MPEG2TSExtractor::flags() const {
     return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD;
 }
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 3ba7f62..54f9b95 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -73,8 +73,7 @@
     ALOGI("loaded default module %s, handle %d", descriptor.properties.description,
                                                  descriptor.handle);
 
-    sp<ISoundTriggerClient> client;
-    sp<Module> module = new Module(this, halInterface, descriptor, client);
+    sp<Module> module = new Module(this, halInterface, descriptor);
     mModules.add(descriptor.handle, module);
     mCallbackThread = new CallbackThread(this);
 }
@@ -126,11 +125,13 @@
     }
     sp<Module> module = mModules.valueAt(index);
 
-    module->setClient(client);
-    IInterface::asBinder(client)->linkToDeath(module);
-    moduleInterface = module;
+    sp<ModuleClient> moduleClient = module->addClient(client);
+    if (moduleClient == 0) {
+        return NO_INIT;
+    }
 
-    module->setCaptureState_l(mCaptureState);
+    moduleClient->setCaptureState_l(mCaptureState);
+    moduleInterface = moduleClient;
 
     return NO_ERROR;
 }
@@ -147,14 +148,6 @@
 }
 
 
-void SoundTriggerHwService::detachModule(const sp<Module>& module)
-{
-    ALOGV("detachModule");
-    AutoMutex lock(mServiceLock);
-    module->clearClient();
-}
-
-
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 60000;
 
@@ -276,8 +269,10 @@
          return;
      }
 
-     sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
-                                                  eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 // static
@@ -329,8 +324,10 @@
     if (strongModule == 0) {
         return;
     }
-    sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
-                                                 eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 
@@ -366,8 +363,23 @@
     if (strongModule == 0) {
         return;
     }
-    sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
-                                                 eventMemory, strongModule));
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+                                                        eventMemory);
+    callbackEvent->setModule(strongModule);
+    sendCallbackEvent_l(callbackEvent);
+}
+
+void SoundTriggerHwService::sendServiceStateEvent_l(sound_trigger_service_state_t state,
+                                                    ModuleClient *moduleClient)
+{
+    sp<IMemory> eventMemory = prepareServiceStateEvent_l(state);
+    if (eventMemory == 0) {
+        return;
+    }
+    sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE,
+                                                        eventMemory);
+    callbackEvent->setModuleClient(moduleClient);
+    sendCallbackEvent_l(callbackEvent);
 }
 
 // call with mServiceLock held
@@ -380,14 +392,25 @@
 {
     ALOGV("onCallbackEvent");
     sp<Module> module;
+    sp<ModuleClient> moduleClient;
     {
         AutoMutex lock(mServiceLock);
+        //CallbackEvent is either for Module or ModuleClient
         module = event->mModule.promote();
         if (module == 0) {
-            return;
+            moduleClient = event->mModuleClient.promote();
+            if (moduleClient == 0) {
+                return;
+            }
         }
     }
-    module->onCallbackEvent(event);
+    if (module != 0) {
+        ALOGV("onCallbackEvent for module");
+        module->onCallbackEvent(event);
+    } else if (moduleClient != 0) {
+        ALOGV("onCallbackEvent for moduleClient");
+        moduleClient->onCallbackEvent(event);
+    }
     {
         AutoMutex lock(mServiceLock);
         // clear now to execute with mServiceLock locked
@@ -457,9 +480,8 @@
     mCallbackCond.signal();
 }
 
-SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory,
-                                                    wp<Module> module)
-    : mType(type), mMemory(memory), mModule(module)
+SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory)
+    : mType(type), mMemory(memory)
 {
 }
 
@@ -473,25 +495,59 @@
 
 SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service,
                                       const sp<SoundTriggerHalInterface>& halInterface,
-                                      sound_trigger_module_descriptor descriptor,
-                                      const sp<ISoundTriggerClient>& client)
+                                      sound_trigger_module_descriptor descriptor)
  : mService(service), mHalInterface(halInterface), mDescriptor(descriptor),
-   mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
+   mServiceState(SOUND_TRIGGER_STATE_NO_INIT)
 {
 }
 
 SoundTriggerHwService::Module::~Module() {
+    mModuleClients.clear();
 }
 
-void SoundTriggerHwService::Module::detach() {
-    ALOGV("detach()");
-    if (!captureHotwordAllowed()) {
+sp<SoundTriggerHwService::ModuleClient>
+SoundTriggerHwService::Module::addClient(const sp<ISoundTriggerClient>& client)
+{
+    AutoMutex lock(mLock);
+    sp<ModuleClient> moduleClient;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i]->client() == client) {
+            // Client already present, reuse client
+            return moduleClient;
+        }
+    }
+    moduleClient = new ModuleClient(this, client);
+
+    ALOGV("addClient() client %p", moduleClient.get());
+    mModuleClients.add(moduleClient);
+
+    return moduleClient;
+}
+
+void SoundTriggerHwService::Module::detach(const sp<ModuleClient>& moduleClient)
+{
+    ALOGV("Module::detach()");
+    AutoMutex lock(mLock);
+    ssize_t index = -1;
+
+    for (size_t i = 0; i < mModuleClients.size(); i++) {
+        if (mModuleClients[i] == moduleClient) {
+            index = i;
+            break;
+        }
+    }
+    if (index == -1) {
         return;
     }
-    {
-        AutoMutex lock(mLock);
-        for (size_t i = 0; i < mModels.size(); i++) {
-            sp<Model> model = mModels.valueAt(i);
+
+    ALOGV("remove client %p", moduleClient.get());
+    mModuleClients.removeAt(index);
+
+    for (size_t i = 0; i < mModels.size(); i++) {
+        sp<Model> model = mModels.valueAt(i);
+        if (moduleClient == model->mModuleClient) {
+            mModels.removeItemsAt(i);
             ALOGV("detach() unloading model %d", model->mHandle);
             if (mHalInterface != 0) {
                 if (model->mState == Model::STATE_ACTIVE) {
@@ -499,29 +555,20 @@
                 }
                 mHalInterface->unloadSoundModel(model->mHandle);
             }
+            AudioSystem::releaseSoundTriggerSession(model->mCaptureSession);
+            mHalInterface->unloadSoundModel(model->mHandle);
         }
-        mModels.clear();
     }
-    if (mClient != 0) {
-        IInterface::asBinder(mClient)->unlinkToDeath(this);
-    }
-    sp<SoundTriggerHwService> service = mService.promote();
-    if (service == 0) {
-        return;
-    }
-    service->detachModule(this);
 }
 
 status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory,
-                                sound_model_handle_t *handle)
+                                                       sp<ModuleClient> moduleClient,
+                                                       sound_model_handle_t *handle)
 {
     ALOGV("loadSoundModel() handle");
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
     if (modelMemory == 0 || modelMemory->pointer() == NULL) {
         ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()");
         return BAD_VALUE;
@@ -569,7 +616,8 @@
         return status;
     }
 
-    sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type);
+    sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type,
+                                moduleClient);
     mModels.replaceValueFor(*handle, model);
 
     return status;
@@ -578,10 +626,6 @@
 status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle)
 {
     ALOGV("unloadSoundModel() model handle %d", handle);
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     AutoMutex lock(mLock);
     return unloadSoundModel_l(handle);
 }
@@ -612,10 +656,6 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     if (dataMemory == 0 || dataMemory->pointer() == NULL) {
         ALOGE("startRecognition() dataMemory is 0 or has NULL pointer()");
         return BAD_VALUE;
@@ -668,10 +708,6 @@
     if (mHalInterface == 0) {
         return NO_INIT;
     }
-    if (!captureHotwordAllowed()) {
-        return PERMISSION_DENIED;
-    }
-
     AutoMutex lock(mLock);
     sp<Model> model = getModel(handle);
     if (model == 0) {
@@ -686,7 +722,6 @@
     return NO_ERROR;
 }
 
-
 void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event)
 {
     ALOGV("onCallbackEvent type %d", event->mType);
@@ -696,8 +731,8 @@
     if (eventMemory == 0 || eventMemory->pointer() == NULL) {
         return;
     }
-    if (mClient == 0) {
-        ALOGI("%s mClient == 0", __func__);
+    if (mModuleClients.isEmpty()) {
+        ALOGI("%s no clients", __func__);
         return;
     }
 
@@ -720,7 +755,7 @@
 
             recognitionEvent->capture_session = model->mCaptureSession;
             model->mState = Model::STATE_IDLE;
-            client = mClient;
+            client = model->mModuleClient->client();
         }
         if (client != 0) {
             client->onRecognitionEvent(eventMemory);
@@ -737,20 +772,24 @@
                 ALOGW("%s model == 0", __func__);
                 return;
             }
-            client = mClient;
+            client = model->mModuleClient->client();
         }
         if (client != 0) {
             client->onSoundModelEvent(eventMemory);
         }
     } break;
     case CallbackEvent::TYPE_SERVICE_STATE: {
-        sp<ISoundTriggerClient> client;
+        Vector< sp<ISoundTriggerClient> > clients;
         {
             AutoMutex lock(mLock);
-            client = mClient;
+            for (size_t i = 0; i < mModuleClients.size(); i++) {
+                if (mModuleClients[i] != 0) {
+                    clients.add(mModuleClients[i]->client());
+                }
+            }
         }
-        if (client != 0) {
-            client->onServiceStateChange(eventMemory);
+        for (size_t i = 0; i < clients.size(); i++) {
+            clients[i]->onServiceStateChange(eventMemory);
         }
     } break;
     default:
@@ -769,12 +808,6 @@
     return model;
 }
 
-void SoundTriggerHwService::Module::binderDied(
-    const wp<IBinder> &who __unused) {
-    ALOGW("client binder died for module %d", mDescriptor.handle);
-    detach();
-}
-
 // Called with mServiceLock held
 void SoundTriggerHwService::Module::setCaptureState_l(bool active)
 {
@@ -858,8 +891,10 @@
     }
 
     for (size_t i = 0; i < events.size(); i++) {
-        service->sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, events[i],
-                                                     this));
+        sp<CallbackEvent> callbackEvent = new CallbackEvent(CallbackEvent::TYPE_RECOGNITION,
+                                                            events[i]);
+        callbackEvent->setModule(this);
+        service->sendCallbackEvent_l(callbackEvent);
     }
 
 exit:
@@ -869,17 +904,170 @@
 
 SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session,
                                     audio_io_handle_t ioHandle, audio_devices_t device,
-                                    sound_trigger_sound_model_type_t type) :
+                                    sound_trigger_sound_model_type_t type,
+                                    sp<ModuleClient>& moduleClient) :
     mHandle(handle), mState(STATE_IDLE), mCaptureSession(session),
-    mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type)
+    mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type),
+    mModuleClient(moduleClient)
 {
-
 }
 
-status_t SoundTriggerHwService::Module::dump(int fd __unused,
-                                             const Vector<String16>& args __unused) {
+#undef LOG_TAG
+#define LOG_TAG "SoundTriggerHwService::ModuleClient"
+
+SoundTriggerHwService::ModuleClient::ModuleClient(const sp<Module>& module,
+                                                  const sp<ISoundTriggerClient>& client)
+ : mModule(module), mClient(client)
+{
+}
+
+void SoundTriggerHwService::ModuleClient::onFirstRef()
+{
+    IInterface::asBinder(mClient)->linkToDeath(this);
+}
+
+SoundTriggerHwService::ModuleClient::~ModuleClient()
+{
+}
+
+status_t SoundTriggerHwService::ModuleClient::dump(int fd __unused,
+                                                   const Vector<String16>& args __unused) {
     String8 result;
     return NO_ERROR;
 }
 
+void SoundTriggerHwService::ModuleClient::detach() {
+    ALOGV("detach()");
+    if (!captureHotwordAllowed()) {
+        return;
+    }
+
+    {
+        AutoMutex lock(mLock);
+        if (mClient != 0) {
+            IInterface::asBinder(mClient)->unlinkToDeath(this);
+            mClient.clear();
+        }
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return;
+    }
+    module->detach(this);
+}
+
+status_t SoundTriggerHwService::ModuleClient::loadSoundModel(const sp<IMemory>& modelMemory,
+                                sound_model_handle_t *handle)
+{
+    ALOGV("loadSoundModel() handle");
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->loadSoundModel(modelMemory, this, handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::unloadSoundModel(sound_model_handle_t handle)
+{
+    ALOGV("unloadSoundModel() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->unloadSoundModel(handle);
+}
+
+status_t SoundTriggerHwService::ModuleClient::startRecognition(sound_model_handle_t handle,
+                                 const sp<IMemory>& dataMemory)
+{
+    ALOGV("startRecognition() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->startRecognition(handle, dataMemory);
+}
+
+status_t SoundTriggerHwService::ModuleClient::stopRecognition(sound_model_handle_t handle)
+{
+    ALOGV("stopRecognition() model handle %d", handle);
+    if (!captureHotwordAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return NO_INIT;
+    }
+    return module->stopRecognition(handle);
+}
+
+void SoundTriggerHwService::ModuleClient::setCaptureState_l(bool active)
+{
+    ALOGV("ModuleClient::setCaptureState_l %d", active);
+    sp<SoundTriggerHwService> service;
+    sound_trigger_service_state_t state;
+
+    sp<Module> module = mModule.promote();
+    if (module == 0) {
+        return;
+    }
+    {
+        AutoMutex lock(mLock);
+        state = (active && !module->isConcurrentCaptureAllowed()) ?
+                                        SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED;
+
+        service = module->service().promote();
+        if (service == 0) {
+            return;
+        }
+    }
+    service->sendServiceStateEvent_l(state, this);
+}
+
+void SoundTriggerHwService::ModuleClient::onCallbackEvent(const sp<CallbackEvent>& event)
+{
+    ALOGV("ModuleClient onCallbackEvent type %d", event->mType);
+
+    sp<IMemory> eventMemory = event->mMemory;
+
+    if (eventMemory == 0 || eventMemory->pointer() == NULL) {
+        return;
+    }
+
+    switch (event->mType) {
+    case CallbackEvent::TYPE_SERVICE_STATE: {
+        sp<ISoundTriggerClient> client;
+        {
+            AutoMutex lock(mLock);
+            client = mClient;
+        }
+        if (client !=0 ) {
+            client->onServiceStateChange(eventMemory);
+        }
+    } break;
+    default:
+        LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType);
+    }
+}
+
+void SoundTriggerHwService::ModuleClient::binderDied(
+    const wp<IBinder> &who __unused) {
+    ALOGW("client binder died for client %p", this);
+    detach();
+}
+
 }; // namespace android
diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h
index 7f7d0cc..60ebb35 100644
--- a/services/soundtrigger/SoundTriggerHwService.h
+++ b/services/soundtrigger/SoundTriggerHwService.h
@@ -39,6 +39,7 @@
     friend class BinderService<SoundTriggerHwService>;
 public:
     class Module;
+    class ModuleClient;
 
     static char const* getServiceName() { return "media.sound_trigger_hw"; }
 
@@ -69,7 +70,8 @@
         };
 
         Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle,
-              audio_devices_t device, sound_trigger_sound_model_type_t type);
+              audio_devices_t device, sound_trigger_sound_model_type_t type,
+              sp<ModuleClient>& moduleClient);
         ~Model() {}
 
         sound_model_handle_t    mHandle;
@@ -79,6 +81,7 @@
         audio_devices_t         mCaptureDevice;
         sound_trigger_sound_model_type_t mType;
         struct sound_trigger_recognition_config mConfig;
+        sp<ModuleClient>        mModuleClient;
     };
 
     class CallbackEvent : public RefBase {
@@ -88,27 +91,76 @@
             TYPE_SOUNDMODEL,
             TYPE_SERVICE_STATE,
         } event_type;
-        CallbackEvent(event_type type, sp<IMemory> memory, wp<Module> module);
+        CallbackEvent(event_type type, sp<IMemory> memory);
 
         virtual             ~CallbackEvent();
 
+        void setModule(wp<Module> module) { mModule = module; }
+        void setModuleClient(wp<ModuleClient> moduleClient) { mModuleClient = moduleClient; }
+
         event_type mType;
         sp<IMemory> mMemory;
         wp<Module> mModule;
+        wp<ModuleClient> mModuleClient;
     };
 
-    class Module : public virtual RefBase,
-                   public BnSoundTrigger,
-                   public IBinder::DeathRecipient     {
+    class Module : public RefBase {
     public:
 
        Module(const sp<SoundTriggerHwService>& service,
               const sp<SoundTriggerHalInterface>& halInterface,
-              sound_trigger_module_descriptor descriptor,
-              const sp<ISoundTriggerClient>& client);
+              sound_trigger_module_descriptor descriptor);
 
        virtual ~Module();
 
+       virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
+                                       sp<ModuleClient> moduleClient,
+                                       sound_model_handle_t *handle);
+
+       virtual status_t unloadSoundModel(sound_model_handle_t handle);
+
+       virtual status_t startRecognition(sound_model_handle_t handle,
+                                         const sp<IMemory>& dataMemory);
+       virtual status_t stopRecognition(sound_model_handle_t handle);
+
+       sp<SoundTriggerHalInterface> halInterface() const { return mHalInterface; }
+       struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
+       wp<SoundTriggerHwService> service() const { return mService; }
+       bool isConcurrentCaptureAllowed() const { return mDescriptor.properties.concurrent_capture; }
+
+       sp<Model> getModel(sound_model_handle_t handle);
+
+       void setCaptureState_l(bool active);
+
+       sp<ModuleClient> addClient(const sp<ISoundTriggerClient>& client);
+
+       void detach(const sp<ModuleClient>& moduleClient);
+
+       void onCallbackEvent(const sp<CallbackEvent>& event);
+
+    private:
+
+        status_t unloadSoundModel_l(sound_model_handle_t handle);
+
+        Mutex                                  mLock;
+        wp<SoundTriggerHwService>              mService;
+        sp<SoundTriggerHalInterface>           mHalInterface;
+        struct sound_trigger_module_descriptor mDescriptor;
+        Vector< sp<ModuleClient> >             mModuleClients;
+        DefaultKeyedVector< sound_model_handle_t, sp<Model> >     mModels;
+        sound_trigger_service_state_t          mServiceState;
+    }; // class Module
+
+    class ModuleClient : public virtual RefBase,
+                         public BnSoundTrigger,
+                         public IBinder::DeathRecipient {
+    public:
+
+       ModuleClient(const sp<Module>& module,
+              const sp<ISoundTriggerClient>& client);
+
+       virtual ~ModuleClient();
+
        virtual void detach();
 
        virtual status_t loadSoundModel(const sp<IMemory>& modelMemory,
@@ -122,35 +174,23 @@
 
        virtual status_t dump(int fd, const Vector<String16>& args);
 
-
-       struct sound_trigger_module_descriptor descriptor() { return mDescriptor; }
-       void setClient(const sp<ISoundTriggerClient>& client) { mClient = client; }
-       void clearClient() { mClient.clear(); }
-       sp<ISoundTriggerClient> client() const { return mClient; }
-       wp<SoundTriggerHwService> service() const { return mService; }
-
-       void onCallbackEvent(const sp<CallbackEvent>& event);
-
-       sp<Model> getModel(sound_model_handle_t handle);
-
-       void setCaptureState_l(bool active);
+       virtual void onFirstRef();
 
        // IBinder::DeathRecipient implementation
        virtual void        binderDied(const wp<IBinder> &who);
 
+       void onCallbackEvent(const sp<CallbackEvent>& event);
+
+       void setCaptureState_l(bool active);
+
+       sp<ISoundTriggerClient> client() const { return mClient; }
+
     private:
 
-       status_t unloadSoundModel_l(sound_model_handle_t handle);
-
-
-        Mutex                                  mLock;
-        wp<SoundTriggerHwService>              mService;
-        sp<SoundTriggerHalInterface>           mHalInterface;
-        struct sound_trigger_module_descriptor mDescriptor;
-        sp<ISoundTriggerClient>                mClient;
-        DefaultKeyedVector< sound_model_handle_t, sp<Model> >     mModels;
-        sound_trigger_service_state_t          mServiceState;
-    }; // class Module
+        mutable Mutex               mLock;
+        wp<Module>                  mModule;
+        sp<ISoundTriggerClient>     mClient;
+    }; // class ModuleClient
 
     class CallbackThread : public Thread {
     public:
@@ -175,8 +215,6 @@
         Vector< sp<CallbackEvent> > mEventQueue;
     };
 
-           void detachModule(const sp<Module>& module);
-
     static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie);
            sp<IMemory> prepareRecognitionEvent_l(struct sound_trigger_recognition_event *event);
            void sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module);
@@ -187,6 +225,8 @@
 
            sp<IMemory> prepareServiceStateEvent_l(sound_trigger_service_state_t state);
            void sendServiceStateEvent_l(sound_trigger_service_state_t state, Module *module);
+           void sendServiceStateEvent_l(sound_trigger_service_state_t state,
+                                        ModuleClient *moduleClient);
 
            void sendCallbackEvent_l(const sp<CallbackEvent>& event);
            void onCallbackEvent(const sp<CallbackEvent>& event);