audio policy: add routing update client interface

Added IAudioPolicyServiceClient client binder interface
for client process to receive notifications from AudioPolicyService
when audio ports are added/removed or audio patches created/released.

The audio patches owned by a given client are automatically released when
this client binder dies.

Bug: 14815883.

Change-Id: I6013f6aec03b50565cffb1ad2cd1f0f8852032c5
diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index 8225e36..cbab841 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -195,4 +195,14 @@
     return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
 }
 
+void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
+{
+    mAudioPolicyService->onAudioPortListUpdate();
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate()
+{
+    mAudioPolicyService->onAudioPatchListUpdate();
+}
+
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 98ad1d4..e0c7f61 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -273,6 +273,9 @@
     virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                        int delayMs) = 0;
 
+    virtual void onAudioPortListUpdate() = 0;
+
+    virtual void onAudioPatchListUpdate() = 0;
 };
 
 extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 905418b..b5b26d3 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -286,6 +286,7 @@
                    device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
             device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
         } else {
+            mpClientInterface->onAudioPortListUpdate();
             return NO_ERROR;
         }
     }  // end if is output device
@@ -343,6 +344,7 @@
 
         closeAllInputs();
 
+        mpClientInterface->onAudioPortListUpdate();
         return NO_ERROR;
     } // end if is input device
 
@@ -754,6 +756,7 @@
         }
         mPreviousOutputs = mOutputs;
         ALOGV("getOutput() returns new direct output %d", output);
+        mpClientInterface->onAudioPortListUpdate();
         return output;
     }
 
@@ -986,6 +989,7 @@
             if (dstOutput != mPrimaryOutput) {
                 mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
             }
+            mpClientInterface->onAudioPortListUpdate();
         }
     }
 }
@@ -1067,6 +1071,7 @@
         return 0;
     }
     addInput(input, inputDesc);
+    mpClientInterface->onAudioPortListUpdate();
     return input;
 }
 
@@ -1152,6 +1157,7 @@
     delete mInputs.valueAt(index);
     mInputs.removeItem(input);
     nextAudioPortGeneration();
+    mpClientInterface->onAudioPortListUpdate();
     ALOGV("releaseInput() exit");
 }
 
@@ -1904,6 +1910,7 @@
                 patchDesc->mAfPatchHandle = afPatchHandle;
                 *handle = patchDesc->mHandle;
                 nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
             } else {
                 ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
                 status);
@@ -1967,6 +1974,7 @@
                                                               status, patchDesc->mAfPatchHandle);
             removeAudioPatch(patchDesc->mHandle);
             nextAudioPortGeneration();
+            mpClientInterface->onAudioPatchListUpdate();
         } else {
             return BAD_VALUE;
         }
@@ -3584,6 +3592,7 @@
                 }
                 outputDesc->mPatchHandle = patchDesc->mHandle;
                 nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
             }
         }
     }
@@ -3614,6 +3623,7 @@
     outputDesc->mPatchHandle = 0;
     removeAudioPatch(patchDesc->mHandle);
     nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
     return status;
 }
 
@@ -3669,6 +3679,7 @@
                 }
                 inputDesc->mPatchHandle = patchDesc->mHandle;
                 nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
             }
         }
     }
@@ -3694,6 +3705,7 @@
     inputDesc->mPatchHandle = 0;
     removeAudioPatch(patchDesc->mHandle);
     nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
     return status;
 }
 
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index ea573a4..f3d92ed 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -148,6 +148,61 @@
     delete mAudioPolicyManager;
     delete mAudioPolicyClient;
 #endif
+
+    mNotificationClients.clear();
+}
+
+// A notification client is always registered by AudioSystem when the client process
+// connects to AudioPolicyService.
+void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+{
+
+    Mutex::Autolock _l(mLock);
+
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (mNotificationClients.indexOfKey(uid) < 0) {
+        sp<NotificationClient> notificationClient = new NotificationClient(this,
+                                                                           client,
+                                                                           uid);
+        ALOGV("registerClient() client %p, uid %d", client.get(), uid);
+
+        mNotificationClients.add(uid, notificationClient);
+
+        sp<IBinder> binder = client->asBinder();
+        binder->linkToDeath(notificationClient);
+    }
+}
+
+// removeNotificationClient() is called when the client process dies.
+void AudioPolicyService::removeNotificationClient(uid_t uid)
+{
+    Mutex::Autolock _l(mLock);
+
+    mNotificationClients.removeItem(uid);
+
+#ifndef USE_LEGACY_AUDIO_POLICY
+        if (mAudioPolicyManager) {
+            mAudioPolicyManager->clearAudioPatches(uid);
+        }
+#endif
+}
+
+void AudioPolicyService::onAudioPortListUpdate()
+{
+    mOutputCommandThread->updateAudioPortListCommand();
+}
+
+void AudioPolicyService::doOnAudioPortListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::onAudioPatchListUpdate()
+{
+    mOutputCommandThread->updateAudioPatchListCommand();
 }
 
 status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -163,6 +218,47 @@
     return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs);
 }
 
+void AudioPolicyService::doOnAudioPatchListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPatchListUpdate();
+    }
+}
+
+AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
+                                                     const sp<IAudioPolicyServiceClient>& client,
+                                                     uid_t uid)
+    : mService(service), mUid(uid), mAudioPolicyServiceClient(client)
+{
+}
+
+AudioPolicyService::NotificationClient::~NotificationClient()
+{
+}
+
+void AudioPolicyService::NotificationClient::binderDied(const wp<IBinder>& who __unused)
+{
+    sp<NotificationClient> keep(this);
+    sp<AudioPolicyService> service = mService.promote();
+    if (service != 0) {
+        service->removeNotificationClient(mUid);
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPortListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPatchListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPatchListUpdate();
+    }
+}
 
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
     ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
@@ -390,6 +486,26 @@
                         command->mStatus = af->releaseAudioPatch(data->mHandle);
                     }
                     } break;
+                case UPDATE_AUDIOPORT_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio port list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPortListUpdate();
+                    mLock.lock();
+                    }break;
+                case UPDATE_AUDIOPATCH_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio patch list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPatchListUpdate();
+                    mLock.lock();
+                    }break;
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -584,6 +700,22 @@
     return sendCommand(command, delayMs);
 }
 
+void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPORT_LIST;
+    ALOGV("AudioCommandThread() adding update audio port list");
+    sendCommand(command);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
+{
+    sp<AudioCommand>command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPATCH_LIST;
+    ALOGV("AudioCommandThread() adding update audio patch list");
+    sendCommand(command);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
@@ -602,7 +734,6 @@
     return command->mStatus;
 }
 
-
 // insertCommand_l() must be called with mLock held
 void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& command, int delayMs)
 {
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 9f88b1e..a579d1d 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -154,6 +154,8 @@
                                       unsigned int *generation);
     virtual status_t setAudioPortConfig(const struct audio_port_config *config);
 
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+
             status_t doStopOutput(audio_io_handle_t output,
                                   audio_stream_type_t stream,
                                   int session = 0);
@@ -164,6 +166,11 @@
                                       int delayMs);
             status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
                                              int delayMs);
+            void removeNotificationClient(uid_t uid);
+            void onAudioPortListUpdate();
+            void doOnAudioPortListUpdate();
+            void onAudioPatchListUpdate();
+            void doOnAudioPatchListUpdate();
 
 private:
                         AudioPolicyService() ANDROID_API;
@@ -192,6 +199,8 @@
             RELEASE_OUTPUT,
             CREATE_AUDIO_PATCH,
             RELEASE_AUDIO_PATCH,
+            UPDATE_AUDIOPORT_LIST,
+            UPDATE_AUDIOPATCH_LIST
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -223,6 +232,8 @@
                                                         int delayMs);
                     status_t    releaseAudioPatchCommand(audio_patch_handle_t handle,
                                                          int delayMs);
+                    void        updateAudioPortListCommand();
+                    void        updateAudioPatchListCommand();
 
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
@@ -454,10 +465,36 @@
         virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                            int delayMs);
 
+        virtual void onAudioPortListUpdate();
+        virtual void onAudioPatchListUpdate();
+
      private:
         AudioPolicyService *mAudioPolicyService;
     };
 
+    // --- Notification Client ---
+    class NotificationClient : public IBinder::DeathRecipient {
+    public:
+                            NotificationClient(const sp<AudioPolicyService>& service,
+                                                const sp<IAudioPolicyServiceClient>& client,
+                                                uid_t uid);
+        virtual             ~NotificationClient();
+
+                            void        onAudioPortListUpdate();
+                            void        onAudioPatchListUpdate();
+
+                // IBinder::DeathRecipient
+                virtual     void        binderDied(const wp<IBinder>& who);
+
+    private:
+                            NotificationClient(const NotificationClient&);
+                            NotificationClient& operator = (const NotificationClient&);
+
+        const wp<AudioPolicyService>        mService;
+        const uid_t                         mUid;
+        const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+    };
+
     static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
 
     void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
@@ -494,6 +531,8 @@
 
     KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
     KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
+
+    DefaultKeyedVector< uid_t, sp<NotificationClient> >    mNotificationClients;
 };
 
 }; // namespace android