Home | History | Annotate | Download | only in audioflinger
      1 /*
      2 **
      3 ** Copyright 2014, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 
     18 
     19 #define LOG_TAG "AudioFlinger::PatchPanel"
     20 //#define LOG_NDEBUG 0
     21 
     22 #include "Configuration.h"
     23 #include <utils/Log.h>
     24 #include <audio_utils/primitives.h>
     25 
     26 #include "AudioFlinger.h"
     27 #include "ServiceUtilities.h"
     28 #include <media/AudioParameter.h>
     29 
     30 // ----------------------------------------------------------------------------
     31 
     32 // Note: the following macro is used for extremely verbose logging message.  In
     33 // order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
     34 // 0; but one side effect of this is to turn all LOGV's as well.  Some messages
     35 // are so verbose that we want to suppress them even when we have ALOG_ASSERT
     36 // turned on.  Do not uncomment the #def below unless you really know what you
     37 // are doing and want to see all of the extremely verbose messages.
     38 //#define VERY_VERY_VERBOSE_LOGGING
     39 #ifdef VERY_VERY_VERBOSE_LOGGING
     40 #define ALOGVV ALOGV
     41 #else
     42 #define ALOGVV(a...) do { } while(0)
     43 #endif
     44 
     45 namespace android {
     46 
     47 /* List connected audio ports and their attributes */
     48 status_t AudioFlinger::listAudioPorts(unsigned int *num_ports,
     49                                 struct audio_port *ports)
     50 {
     51     Mutex::Autolock _l(mLock);
     52     if (mPatchPanel != 0) {
     53         return mPatchPanel->listAudioPorts(num_ports, ports);
     54     }
     55     return NO_INIT;
     56 }
     57 
     58 /* Get supported attributes for a given audio port */
     59 status_t AudioFlinger::getAudioPort(struct audio_port *port)
     60 {
     61     Mutex::Autolock _l(mLock);
     62     if (mPatchPanel != 0) {
     63         return mPatchPanel->getAudioPort(port);
     64     }
     65     return NO_INIT;
     66 }
     67 
     68 
     69 /* Connect a patch between several source and sink ports */
     70 status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch,
     71                                    audio_patch_handle_t *handle)
     72 {
     73     Mutex::Autolock _l(mLock);
     74     if (mPatchPanel != 0) {
     75         return mPatchPanel->createAudioPatch(patch, handle);
     76     }
     77     return NO_INIT;
     78 }
     79 
     80 /* Disconnect a patch */
     81 status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle)
     82 {
     83     Mutex::Autolock _l(mLock);
     84     if (mPatchPanel != 0) {
     85         return mPatchPanel->releaseAudioPatch(handle);
     86     }
     87     return NO_INIT;
     88 }
     89 
     90 
     91 /* List connected audio ports and they attributes */
     92 status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
     93                                   struct audio_patch *patches)
     94 {
     95     Mutex::Autolock _l(mLock);
     96     if (mPatchPanel != 0) {
     97         return mPatchPanel->listAudioPatches(num_patches, patches);
     98     }
     99     return NO_INIT;
    100 }
    101 
    102 /* Set audio port configuration */
    103 status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config)
    104 {
    105     Mutex::Autolock _l(mLock);
    106     if (mPatchPanel != 0) {
    107         return mPatchPanel->setAudioPortConfig(config);
    108     }
    109     return NO_INIT;
    110 }
    111 
    112 
    113 AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger)
    114                                    : mAudioFlinger(audioFlinger)
    115 {
    116 }
    117 
    118 AudioFlinger::PatchPanel::~PatchPanel()
    119 {
    120 }
    121 
    122 /* List connected audio ports and their attributes */
    123 status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
    124                                 struct audio_port *ports __unused)
    125 {
    126     ALOGV("listAudioPorts");
    127     return NO_ERROR;
    128 }
    129 
    130 /* Get supported attributes for a given audio port */
    131 status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused)
    132 {
    133     ALOGV("getAudioPort");
    134     return NO_ERROR;
    135 }
    136 
    137 
    138 /* Connect a patch between several source and sink ports */
    139 status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch,
    140                                    audio_patch_handle_t *handle)
    141 {
    142     ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
    143           patch->num_sources, patch->num_sinks, *handle);
    144     status_t status = NO_ERROR;
    145     audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
    146     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    147     if (audioflinger == 0) {
    148         return NO_INIT;
    149     }
    150 
    151     if (handle == NULL || patch == NULL) {
    152         return BAD_VALUE;
    153     }
    154     if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
    155             patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
    156         return BAD_VALUE;
    157     }
    158     // limit number of sources to 1 for now or 2 sources for special cross hw module case.
    159     // only the audio policy manager can request a patch creation with 2 sources.
    160     if (patch->num_sources > 2) {
    161         return INVALID_OPERATION;
    162     }
    163 
    164     if (*handle != AUDIO_PATCH_HANDLE_NONE) {
    165         for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
    166             if (*handle == mPatches[index]->mHandle) {
    167                 ALOGV("createAudioPatch() removing patch handle %d", *handle);
    168                 halHandle = mPatches[index]->mHalHandle;
    169                 mPatches.removeAt(index);
    170                 break;
    171             }
    172         }
    173     }
    174 
    175     Patch *newPatch = new Patch(patch);
    176 
    177     switch (patch->sources[0].type) {
    178         case AUDIO_PORT_TYPE_DEVICE: {
    179             audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
    180             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    181             if (index < 0) {
    182                 ALOGW("createAudioPatch() bad src hw module %d", srcModule);
    183                 status = BAD_VALUE;
    184                 goto exit;
    185             }
    186             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    187             for (unsigned int i = 0; i < patch->num_sinks; i++) {
    188                 // support only one sink if connection to a mix or across HW modules
    189                 if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
    190                         patch->sinks[i].ext.mix.hw_module != srcModule) &&
    191                         patch->num_sinks > 1) {
    192                     status = INVALID_OPERATION;
    193                     goto exit;
    194                 }
    195                 // reject connection to different sink types
    196                 if (patch->sinks[i].type != patch->sinks[0].type) {
    197                     ALOGW("createAudioPatch() different sink types in same patch not supported");
    198                     status = BAD_VALUE;
    199                     goto exit;
    200                 }
    201                 // limit to connections between devices and input streams for HAL before 3.0
    202                 if (patch->sinks[i].ext.mix.hw_module == srcModule &&
    203                         (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
    204                         (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
    205                     ALOGW("createAudioPatch() invalid sink type %d for device source",
    206                           patch->sinks[i].type);
    207                     status = BAD_VALUE;
    208                     goto exit;
    209                 }
    210             }
    211 
    212             if (patch->sinks[0].ext.device.hw_module != srcModule) {
    213                 // limit to device to device connection if not on same hw module
    214                 if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
    215                     ALOGW("createAudioPatch() invalid sink type for cross hw module");
    216                     status = INVALID_OPERATION;
    217                     goto exit;
    218                 }
    219                 // special case num sources == 2 -=> reuse an exiting output mix to connect to the
    220                 // sink
    221                 if (patch->num_sources == 2) {
    222                     if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
    223                             patch->sinks[0].ext.device.hw_module !=
    224                                     patch->sources[1].ext.mix.hw_module) {
    225                         ALOGW("createAudioPatch() invalid source combination");
    226                         status = INVALID_OPERATION;
    227                         goto exit;
    228                     }
    229 
    230                     sp<ThreadBase> thread =
    231                             audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle);
    232                     newPatch->mPlaybackThread = (MixerThread *)thread.get();
    233                     if (thread == 0) {
    234                         ALOGW("createAudioPatch() cannot get playback thread");
    235                         status = INVALID_OPERATION;
    236                         goto exit;
    237                     }
    238                 } else {
    239                     audio_config_t config = AUDIO_CONFIG_INITIALIZER;
    240                     audio_devices_t device = patch->sinks[0].ext.device.type;
    241                     String8 address = String8(patch->sinks[0].ext.device.address);
    242                     audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
    243                     newPatch->mPlaybackThread = audioflinger->openOutput_l(
    244                                                              patch->sinks[0].ext.device.hw_module,
    245                                                              &output,
    246                                                              &config,
    247                                                              device,
    248                                                              address,
    249                                                              AUDIO_OUTPUT_FLAG_NONE);
    250                     ALOGV("audioflinger->openOutput_l() returned %p",
    251                                           newPatch->mPlaybackThread.get());
    252                     if (newPatch->mPlaybackThread == 0) {
    253                         status = NO_MEMORY;
    254                         goto exit;
    255                     }
    256                 }
    257                 uint32_t channelCount = newPatch->mPlaybackThread->channelCount();
    258                 audio_devices_t device = patch->sources[0].ext.device.type;
    259                 String8 address = String8(patch->sources[0].ext.device.address);
    260                 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
    261                 audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
    262                 config.sample_rate = newPatch->mPlaybackThread->sampleRate();
    263                 config.channel_mask = inChannelMask;
    264                 config.format = newPatch->mPlaybackThread->format();
    265                 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
    266                 newPatch->mRecordThread = audioflinger->openInput_l(srcModule,
    267                                                                     &input,
    268                                                                     &config,
    269                                                                     device,
    270                                                                     address,
    271                                                                     AUDIO_SOURCE_MIC,
    272                                                                     AUDIO_INPUT_FLAG_NONE);
    273                 ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x",
    274                       newPatch->mRecordThread.get(), inChannelMask);
    275                 if (newPatch->mRecordThread == 0) {
    276                     status = NO_MEMORY;
    277                     goto exit;
    278                 }
    279                 status = createPatchConnections(newPatch, patch);
    280                 if (status != NO_ERROR) {
    281                     goto exit;
    282                 }
    283             } else {
    284                 if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    285                     if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
    286                         sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    287                                                                   patch->sinks[0].ext.mix.handle);
    288                         if (thread == 0) {
    289                             ALOGW("createAudioPatch() bad capture I/O handle %d",
    290                                                                   patch->sinks[0].ext.mix.handle);
    291                             status = BAD_VALUE;
    292                             goto exit;
    293                         }
    294                         status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
    295                     } else {
    296                         audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    297                         status = hwDevice->create_audio_patch(hwDevice,
    298                                                                patch->num_sources,
    299                                                                patch->sources,
    300                                                                patch->num_sinks,
    301                                                                patch->sinks,
    302                                                                &halHandle);
    303                     }
    304                 } else {
    305                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    306                                                                     patch->sinks[0].ext.mix.handle);
    307                     if (thread == 0) {
    308                         ALOGW("createAudioPatch() bad capture I/O handle %d",
    309                                                                     patch->sinks[0].ext.mix.handle);
    310                         status = BAD_VALUE;
    311                         goto exit;
    312                     }
    313                     char *address;
    314                     if (strcmp(patch->sources[0].ext.device.address, "") != 0) {
    315                         address = audio_device_address_to_parameter(
    316                                                             patch->sources[0].ext.device.type,
    317                                                             patch->sources[0].ext.device.address);
    318                     } else {
    319                         address = (char *)calloc(1, 1);
    320                     }
    321                     AudioParameter param = AudioParameter(String8(address));
    322                     free(address);
    323                     param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING),
    324                                  (int)patch->sources[0].ext.device.type);
    325                     param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE),
    326                                                      (int)patch->sinks[0].ext.mix.usecase.source);
    327                     ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
    328                                                                       param.toString().string());
    329                     status = thread->setParameters(param.toString());
    330                 }
    331             }
    332         } break;
    333         case AUDIO_PORT_TYPE_MIX: {
    334             audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
    335             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    336             if (index < 0) {
    337                 ALOGW("createAudioPatch() bad src hw module %d", srcModule);
    338                 status = BAD_VALUE;
    339                 goto exit;
    340             }
    341             // limit to connections between devices and output streams
    342             for (unsigned int i = 0; i < patch->num_sinks; i++) {
    343                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
    344                     ALOGW("createAudioPatch() invalid sink type %d for mix source",
    345                           patch->sinks[i].type);
    346                     status = BAD_VALUE;
    347                     goto exit;
    348                 }
    349                 // limit to connections between sinks and sources on same HW module
    350                 if (patch->sinks[i].ext.device.hw_module != srcModule) {
    351                     status = BAD_VALUE;
    352                     goto exit;
    353                 }
    354             }
    355             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    356             sp<ThreadBase> thread =
    357                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
    358             if (thread == 0) {
    359                 ALOGW("createAudioPatch() bad playback I/O handle %d",
    360                           patch->sources[0].ext.mix.handle);
    361                 status = BAD_VALUE;
    362                 goto exit;
    363             }
    364             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    365                 status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
    366             } else {
    367                 audio_devices_t type = AUDIO_DEVICE_NONE;
    368                 for (unsigned int i = 0; i < patch->num_sinks; i++) {
    369                     type |= patch->sinks[i].ext.device.type;
    370                 }
    371                 char *address;
    372                 if (strcmp(patch->sinks[0].ext.device.address, "") != 0) {
    373                     //FIXME: we only support address on first sink with HAL version < 3.0
    374                     address = audio_device_address_to_parameter(
    375                                                                 patch->sinks[0].ext.device.type,
    376                                                                 patch->sinks[0].ext.device.address);
    377                 } else {
    378                     address = (char *)calloc(1, 1);
    379                 }
    380                 AudioParameter param = AudioParameter(String8(address));
    381                 free(address);
    382                 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
    383                 status = thread->setParameters(param.toString());
    384             }
    385 
    386         } break;
    387         default:
    388             status = BAD_VALUE;
    389             goto exit;
    390     }
    391 exit:
    392     ALOGV("createAudioPatch() status %d", status);
    393     if (status == NO_ERROR) {
    394         *handle = audioflinger->nextUniqueId();
    395         newPatch->mHandle = *handle;
    396         newPatch->mHalHandle = halHandle;
    397         mPatches.add(newPatch);
    398         ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
    399     } else {
    400         clearPatchConnections(newPatch);
    401         delete newPatch;
    402     }
    403     return status;
    404 }
    405 
    406 status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch,
    407                                                           const struct audio_patch *audioPatch)
    408 {
    409     // create patch from source device to record thread input
    410     struct audio_patch subPatch;
    411     subPatch.num_sources = 1;
    412     subPatch.sources[0] = audioPatch->sources[0];
    413     subPatch.num_sinks = 1;
    414 
    415     patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]);
    416     subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC;
    417 
    418     status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle);
    419     if (status != NO_ERROR) {
    420         patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    421         return status;
    422     }
    423 
    424     // create patch from playback thread output to sink device
    425     patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]);
    426     subPatch.sinks[0] = audioPatch->sinks[0];
    427     status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle);
    428     if (status != NO_ERROR) {
    429         patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    430         return status;
    431     }
    432 
    433     // use a pseudo LCM between input and output framecount
    434     size_t playbackFrameCount = patch->mPlaybackThread->frameCount();
    435     int playbackShift = __builtin_ctz(playbackFrameCount);
    436     size_t recordFramecount = patch->mRecordThread->frameCount();
    437     int shift = __builtin_ctz(recordFramecount);
    438     if (playbackShift < shift) {
    439         shift = playbackShift;
    440     }
    441     size_t frameCount = (playbackFrameCount * recordFramecount) >> shift;
    442     ALOGV("createPatchConnections() playframeCount %d recordFramecount %d frameCount %d ",
    443           playbackFrameCount, recordFramecount, frameCount);
    444 
    445     // create a special record track to capture from record thread
    446     uint32_t channelCount = patch->mPlaybackThread->channelCount();
    447     audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
    448     audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask();
    449     uint32_t sampleRate = patch->mPlaybackThread->sampleRate();
    450     audio_format_t format = patch->mPlaybackThread->format();
    451 
    452     patch->mPatchRecord = new RecordThread::PatchRecord(
    453                                              patch->mRecordThread.get(),
    454                                              sampleRate,
    455                                              inChannelMask,
    456                                              format,
    457                                              frameCount,
    458                                              NULL,
    459                                              IAudioFlinger::TRACK_DEFAULT);
    460     if (patch->mPatchRecord == 0) {
    461         return NO_MEMORY;
    462     }
    463     status = patch->mPatchRecord->initCheck();
    464     if (status != NO_ERROR) {
    465         return status;
    466     }
    467     patch->mRecordThread->addPatchRecord(patch->mPatchRecord);
    468 
    469     // create a special playback track to render to playback thread.
    470     // this track is given the same buffer as the PatchRecord buffer
    471     patch->mPatchTrack = new PlaybackThread::PatchTrack(
    472                                            patch->mPlaybackThread.get(),
    473                                            sampleRate,
    474                                            outChannelMask,
    475                                            format,
    476                                            frameCount,
    477                                            patch->mPatchRecord->buffer(),
    478                                            IAudioFlinger::TRACK_DEFAULT);
    479     if (patch->mPatchTrack == 0) {
    480         return NO_MEMORY;
    481     }
    482     status = patch->mPatchTrack->initCheck();
    483     if (status != NO_ERROR) {
    484         return status;
    485     }
    486     patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack);
    487 
    488     // tie playback and record tracks together
    489     patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get());
    490     patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get());
    491 
    492     // start capture and playback
    493     patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, 0);
    494     patch->mPatchTrack->start();
    495 
    496     return status;
    497 }
    498 
    499 void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch)
    500 {
    501     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    502     if (audioflinger == 0) {
    503         return;
    504     }
    505 
    506     ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d",
    507           patch->mRecordPatchHandle, patch->mPlaybackPatchHandle);
    508 
    509     if (patch->mPatchRecord != 0) {
    510         patch->mPatchRecord->stop();
    511     }
    512     if (patch->mPatchTrack != 0) {
    513         patch->mPatchTrack->stop();
    514     }
    515     if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
    516         releaseAudioPatch(patch->mRecordPatchHandle);
    517         patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    518     }
    519     if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
    520         releaseAudioPatch(patch->mPlaybackPatchHandle);
    521         patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    522     }
    523     if (patch->mRecordThread != 0) {
    524         if (patch->mPatchRecord != 0) {
    525             patch->mRecordThread->deletePatchRecord(patch->mPatchRecord);
    526             patch->mPatchRecord.clear();
    527         }
    528         audioflinger->closeInputInternal_l(patch->mRecordThread);
    529         patch->mRecordThread.clear();
    530     }
    531     if (patch->mPlaybackThread != 0) {
    532         if (patch->mPatchTrack != 0) {
    533             patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack);
    534             patch->mPatchTrack.clear();
    535         }
    536         // if num sources == 2 we are reusing an existing playback thread so we do not close it
    537         if (patch->mAudioPatch.num_sources != 2) {
    538             audioflinger->closeOutputInternal_l(patch->mPlaybackThread);
    539         }
    540         patch->mPlaybackThread.clear();
    541     }
    542 }
    543 
    544 /* Disconnect a patch */
    545 status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
    546 {
    547     ALOGV("releaseAudioPatch handle %d", handle);
    548     status_t status = NO_ERROR;
    549     size_t index;
    550 
    551     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    552     if (audioflinger == 0) {
    553         return NO_INIT;
    554     }
    555 
    556     for (index = 0; index < mPatches.size(); index++) {
    557         if (handle == mPatches[index]->mHandle) {
    558             break;
    559         }
    560     }
    561     if (index == mPatches.size()) {
    562         return BAD_VALUE;
    563     }
    564     Patch *removedPatch = mPatches[index];
    565     mPatches.removeAt(index);
    566 
    567     struct audio_patch *patch = &removedPatch->mAudioPatch;
    568 
    569     switch (patch->sources[0].type) {
    570         case AUDIO_PORT_TYPE_DEVICE: {
    571             audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
    572             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    573             if (index < 0) {
    574                 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
    575                 status = BAD_VALUE;
    576                 break;
    577             }
    578 
    579             if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE &&
    580                     patch->sinks[0].ext.device.hw_module != srcModule) {
    581                 clearPatchConnections(removedPatch);
    582                 break;
    583             }
    584 
    585             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    586             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    587                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
    588                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    589                                                                     patch->sinks[0].ext.mix.handle);
    590                     if (thread == 0) {
    591                         ALOGW("releaseAudioPatch() bad capture I/O handle %d",
    592                                                                   patch->sinks[0].ext.mix.handle);
    593                         status = BAD_VALUE;
    594                         break;
    595                     }
    596                     status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
    597                 } else {
    598                     audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    599                     status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
    600                 }
    601             } else {
    602                 sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    603                                                                     patch->sinks[0].ext.mix.handle);
    604                 if (thread == 0) {
    605                     ALOGW("releaseAudioPatch() bad capture I/O handle %d",
    606                                                                   patch->sinks[0].ext.mix.handle);
    607                     status = BAD_VALUE;
    608                     break;
    609                 }
    610                 AudioParameter param;
    611                 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
    612                 ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s",
    613                                                                       param.toString().string());
    614                 status = thread->setParameters(param.toString());
    615             }
    616         } break;
    617         case AUDIO_PORT_TYPE_MIX: {
    618             audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
    619             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    620             if (index < 0) {
    621                 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
    622                 status = BAD_VALUE;
    623                 break;
    624             }
    625             sp<ThreadBase> thread =
    626                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
    627             if (thread == 0) {
    628                 ALOGW("releaseAudioPatch() bad playback I/O handle %d",
    629                                                               patch->sources[0].ext.mix.handle);
    630                 status = BAD_VALUE;
    631                 break;
    632             }
    633             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    634             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    635                 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
    636             } else {
    637                 AudioParameter param;
    638                 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0);
    639                 status = thread->setParameters(param.toString());
    640             }
    641         } break;
    642         default:
    643             status = BAD_VALUE;
    644             break;
    645     }
    646 
    647     delete removedPatch;
    648     return status;
    649 }
    650 
    651 
    652 /* List connected audio ports and they attributes */
    653 status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
    654                                   struct audio_patch *patches __unused)
    655 {
    656     ALOGV("listAudioPatches");
    657     return NO_ERROR;
    658 }
    659 
    660 /* Set audio port configuration */
    661 status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
    662 {
    663     ALOGV("setAudioPortConfig");
    664     status_t status = NO_ERROR;
    665 
    666     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    667     if (audioflinger == 0) {
    668         return NO_INIT;
    669     }
    670 
    671     audio_module_handle_t module;
    672     if (config->type == AUDIO_PORT_TYPE_DEVICE) {
    673         module = config->ext.device.hw_module;
    674     } else {
    675         module = config->ext.mix.hw_module;
    676     }
    677 
    678     ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
    679     if (index < 0) {
    680         ALOGW("setAudioPortConfig() bad hw module %d", module);
    681         return BAD_VALUE;
    682     }
    683 
    684     AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    685     if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    686         audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    687         return hwDevice->set_audio_port_config(hwDevice, config);
    688     } else {
    689         return INVALID_OPERATION;
    690     }
    691     return NO_ERROR;
    692 }
    693 
    694 
    695 }; // namespace android
    696