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     status_t status = NO_ERROR;
    143     audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
    144     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    145     if (handle == NULL || patch == NULL) {
    146         return BAD_VALUE;
    147     }
    148     ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d",
    149           patch->num_sources, patch->num_sinks, *handle);
    150     if (audioflinger == 0) {
    151         return NO_INIT;
    152     }
    153 
    154     if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
    155             (patch->num_sinks == 0 && patch->num_sources != 2) ||
    156             patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
    157         return BAD_VALUE;
    158     }
    159     // limit number of sources to 1 for now or 2 sources for special cross hw module case.
    160     // only the audio policy manager can request a patch creation with 2 sources.
    161     if (patch->num_sources > 2) {
    162         return INVALID_OPERATION;
    163     }
    164 
    165     if (*handle != AUDIO_PATCH_HANDLE_NONE) {
    166         for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) {
    167             if (*handle == mPatches[index]->mHandle) {
    168                 ALOGV("createAudioPatch() removing patch handle %d", *handle);
    169                 halHandle = mPatches[index]->mHalHandle;
    170                 Patch *removedPatch = mPatches[index];
    171                 if ((removedPatch->mRecordPatchHandle
    172                         != AUDIO_PATCH_HANDLE_NONE) ||
    173                         (removedPatch->mPlaybackPatchHandle !=
    174                                 AUDIO_PATCH_HANDLE_NONE)) {
    175                     clearPatchConnections(removedPatch);
    176                 }
    177                 mPatches.removeAt(index);
    178                 delete removedPatch;
    179                 break;
    180             }
    181         }
    182     }
    183 
    184     Patch *newPatch = new Patch(patch);
    185 
    186     switch (patch->sources[0].type) {
    187         case AUDIO_PORT_TYPE_DEVICE: {
    188             audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
    189             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    190             if (index < 0) {
    191                 ALOGW("createAudioPatch() bad src hw module %d", srcModule);
    192                 status = BAD_VALUE;
    193                 goto exit;
    194             }
    195             AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    196             for (unsigned int i = 0; i < patch->num_sinks; i++) {
    197                 // support only one sink if connection to a mix or across HW modules
    198                 if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
    199                         patch->sinks[i].ext.mix.hw_module != srcModule) &&
    200                         patch->num_sinks > 1) {
    201                     status = INVALID_OPERATION;
    202                     goto exit;
    203                 }
    204                 // reject connection to different sink types
    205                 if (patch->sinks[i].type != patch->sinks[0].type) {
    206                     ALOGW("createAudioPatch() different sink types in same patch not supported");
    207                     status = BAD_VALUE;
    208                     goto exit;
    209                 }
    210             }
    211 
    212             // manage patches requiring a software bridge
    213             // - special patch request with 2 sources (reuse one existing output mix) OR
    214             // - Device to device AND
    215             //    - source HW module != destination HW module OR
    216             //    - audio HAL version < 3.0
    217             if ((patch->num_sources == 2) ||
    218                 ((patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) &&
    219                  ((patch->sinks[0].ext.device.hw_module != srcModule) ||
    220                   (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0)))) {
    221                 if (patch->num_sources == 2) {
    222                     if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX ||
    223                             (patch->num_sinks != 0 && 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                 audio_devices_t device = patch->sources[0].ext.device.type;
    258                 String8 address = String8(patch->sources[0].ext.device.address);
    259                 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
    260                 // open input stream with source device audio properties if provided or
    261                 // default to peer output stream properties otherwise.
    262                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
    263                     config.sample_rate = patch->sources[0].sample_rate;
    264                 } else {
    265                     config.sample_rate = newPatch->mPlaybackThread->sampleRate();
    266                 }
    267                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
    268                     config.channel_mask = patch->sources[0].channel_mask;
    269                 } else {
    270                     config.channel_mask =
    271                         audio_channel_in_mask_from_count(newPatch->mPlaybackThread->channelCount());
    272                 }
    273                 if (patch->sources[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) {
    274                     config.format = patch->sources[0].format;
    275                 } else {
    276                     config.format = newPatch->mPlaybackThread->format();
    277                 }
    278                 audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
    279                 newPatch->mRecordThread = audioflinger->openInput_l(srcModule,
    280                                                                     &input,
    281                                                                     &config,
    282                                                                     device,
    283                                                                     address,
    284                                                                     AUDIO_SOURCE_MIC,
    285                                                                     AUDIO_INPUT_FLAG_NONE);
    286                 ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x",
    287                       newPatch->mRecordThread.get(), config.channel_mask);
    288                 if (newPatch->mRecordThread == 0) {
    289                     status = NO_MEMORY;
    290                     goto exit;
    291                 }
    292                 status = createPatchConnections(newPatch, patch);
    293                 if (status != NO_ERROR) {
    294                     goto exit;
    295                 }
    296             } else {
    297                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
    298                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    299                                                               patch->sinks[0].ext.mix.handle);
    300                     if (thread == 0) {
    301                         ALOGW("createAudioPatch() bad capture I/O handle %d",
    302                                                               patch->sinks[0].ext.mix.handle);
    303                         status = BAD_VALUE;
    304                         goto exit;
    305                     }
    306                     status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
    307                 } else {
    308                     if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
    309                         status = INVALID_OPERATION;
    310                         goto exit;
    311                     }
    312 
    313                     audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    314                     status = hwDevice->create_audio_patch(hwDevice,
    315                                                            patch->num_sources,
    316                                                            patch->sources,
    317                                                            patch->num_sinks,
    318                                                            patch->sinks,
    319                                                            &halHandle);
    320                 }
    321             }
    322         } break;
    323         case AUDIO_PORT_TYPE_MIX: {
    324             audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
    325             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    326             if (index < 0) {
    327                 ALOGW("createAudioPatch() bad src hw module %d", srcModule);
    328                 status = BAD_VALUE;
    329                 goto exit;
    330             }
    331             // limit to connections between devices and output streams
    332             audio_devices_t type = AUDIO_DEVICE_NONE;
    333             for (unsigned int i = 0; i < patch->num_sinks; i++) {
    334                 if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
    335                     ALOGW("createAudioPatch() invalid sink type %d for mix source",
    336                           patch->sinks[i].type);
    337                     status = BAD_VALUE;
    338                     goto exit;
    339                 }
    340                 // limit to connections between sinks and sources on same HW module
    341                 if (patch->sinks[i].ext.device.hw_module != srcModule) {
    342                     status = BAD_VALUE;
    343                     goto exit;
    344                 }
    345                 type |= patch->sinks[i].ext.device.type;
    346             }
    347             sp<ThreadBase> thread =
    348                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
    349             if (thread == 0) {
    350                 ALOGW("createAudioPatch() bad playback I/O handle %d",
    351                           patch->sources[0].ext.mix.handle);
    352                 status = BAD_VALUE;
    353                 goto exit;
    354             }
    355             if (thread == audioflinger->primaryPlaybackThread_l()) {
    356                 AudioParameter param = AudioParameter();
    357                 param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type);
    358 
    359                 audioflinger->broacastParametersToRecordThreads_l(param.toString());
    360             }
    361 
    362             status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle);
    363         } break;
    364         default:
    365             status = BAD_VALUE;
    366             goto exit;
    367     }
    368 exit:
    369     ALOGV("createAudioPatch() status %d", status);
    370     if (status == NO_ERROR) {
    371         *handle = (audio_patch_handle_t) audioflinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
    372         newPatch->mHandle = *handle;
    373         newPatch->mHalHandle = halHandle;
    374         mPatches.add(newPatch);
    375         ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle);
    376     } else {
    377         clearPatchConnections(newPatch);
    378         delete newPatch;
    379     }
    380     return status;
    381 }
    382 
    383 status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch,
    384                                                           const struct audio_patch *audioPatch)
    385 {
    386     // create patch from source device to record thread input
    387     struct audio_patch subPatch;
    388     subPatch.num_sources = 1;
    389     subPatch.sources[0] = audioPatch->sources[0];
    390     subPatch.num_sinks = 1;
    391 
    392     patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]);
    393     subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC;
    394 
    395     status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle);
    396     if (status != NO_ERROR) {
    397         patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    398         return status;
    399     }
    400 
    401     // create patch from playback thread output to sink device
    402     if (audioPatch->num_sinks != 0) {
    403         patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]);
    404         subPatch.sinks[0] = audioPatch->sinks[0];
    405         status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle);
    406         if (status != NO_ERROR) {
    407             patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    408             return status;
    409         }
    410     } else {
    411         patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    412     }
    413 
    414     // use a pseudo LCM between input and output framecount
    415     size_t playbackFrameCount = patch->mPlaybackThread->frameCount();
    416     int playbackShift = __builtin_ctz(playbackFrameCount);
    417     size_t recordFramecount = patch->mRecordThread->frameCount();
    418     int shift = __builtin_ctz(recordFramecount);
    419     if (playbackShift < shift) {
    420         shift = playbackShift;
    421     }
    422     size_t frameCount = (playbackFrameCount * recordFramecount) >> shift;
    423     ALOGV("createPatchConnections() playframeCount %zu recordFramecount %zu frameCount %zu",
    424           playbackFrameCount, recordFramecount, frameCount);
    425 
    426     // create a special record track to capture from record thread
    427     uint32_t channelCount = patch->mPlaybackThread->channelCount();
    428     audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount);
    429     audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask();
    430     uint32_t sampleRate = patch->mPlaybackThread->sampleRate();
    431     audio_format_t format = patch->mPlaybackThread->format();
    432 
    433     patch->mPatchRecord = new RecordThread::PatchRecord(
    434                                              patch->mRecordThread.get(),
    435                                              sampleRate,
    436                                              inChannelMask,
    437                                              format,
    438                                              frameCount,
    439                                              NULL,
    440                                              IAudioFlinger::TRACK_DEFAULT);
    441     if (patch->mPatchRecord == 0) {
    442         return NO_MEMORY;
    443     }
    444     status = patch->mPatchRecord->initCheck();
    445     if (status != NO_ERROR) {
    446         return status;
    447     }
    448     patch->mRecordThread->addPatchRecord(patch->mPatchRecord);
    449 
    450     // create a special playback track to render to playback thread.
    451     // this track is given the same buffer as the PatchRecord buffer
    452     patch->mPatchTrack = new PlaybackThread::PatchTrack(
    453                                            patch->mPlaybackThread.get(),
    454                                            audioPatch->sources[1].ext.mix.usecase.stream,
    455                                            sampleRate,
    456                                            outChannelMask,
    457                                            format,
    458                                            frameCount,
    459                                            patch->mPatchRecord->buffer(),
    460                                            IAudioFlinger::TRACK_DEFAULT);
    461     if (patch->mPatchTrack == 0) {
    462         return NO_MEMORY;
    463     }
    464     status = patch->mPatchTrack->initCheck();
    465     if (status != NO_ERROR) {
    466         return status;
    467     }
    468     patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack);
    469 
    470     // tie playback and record tracks together
    471     patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get());
    472     patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get());
    473 
    474     // start capture and playback
    475     patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, AUDIO_SESSION_NONE);
    476     patch->mPatchTrack->start();
    477 
    478     return status;
    479 }
    480 
    481 void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch)
    482 {
    483     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    484     if (audioflinger == 0) {
    485         return;
    486     }
    487 
    488     ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d",
    489           patch->mRecordPatchHandle, patch->mPlaybackPatchHandle);
    490 
    491     if (patch->mPatchRecord != 0) {
    492         patch->mPatchRecord->stop();
    493     }
    494     if (patch->mPatchTrack != 0) {
    495         patch->mPatchTrack->stop();
    496     }
    497     if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
    498         releaseAudioPatch(patch->mRecordPatchHandle);
    499         patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    500     }
    501     if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
    502         releaseAudioPatch(patch->mPlaybackPatchHandle);
    503         patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE;
    504     }
    505     if (patch->mRecordThread != 0) {
    506         if (patch->mPatchRecord != 0) {
    507             patch->mRecordThread->deletePatchRecord(patch->mPatchRecord);
    508         }
    509         audioflinger->closeInputInternal_l(patch->mRecordThread);
    510     }
    511     if (patch->mPlaybackThread != 0) {
    512         if (patch->mPatchTrack != 0) {
    513             patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack);
    514         }
    515         // if num sources == 2 we are reusing an existing playback thread so we do not close it
    516         if (patch->mAudioPatch.num_sources != 2) {
    517             audioflinger->closeOutputInternal_l(patch->mPlaybackThread);
    518         }
    519     }
    520     if (patch->mRecordThread != 0) {
    521         if (patch->mPatchRecord != 0) {
    522             patch->mPatchRecord.clear();
    523         }
    524         patch->mRecordThread.clear();
    525     }
    526     if (patch->mPlaybackThread != 0) {
    527         if (patch->mPatchTrack != 0) {
    528             patch->mPatchTrack.clear();
    529         }
    530         patch->mPlaybackThread.clear();
    531     }
    532 
    533 }
    534 
    535 /* Disconnect a patch */
    536 status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle)
    537 {
    538     ALOGV("releaseAudioPatch handle %d", handle);
    539     status_t status = NO_ERROR;
    540     size_t index;
    541 
    542     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    543     if (audioflinger == 0) {
    544         return NO_INIT;
    545     }
    546 
    547     for (index = 0; index < mPatches.size(); index++) {
    548         if (handle == mPatches[index]->mHandle) {
    549             break;
    550         }
    551     }
    552     if (index == mPatches.size()) {
    553         return BAD_VALUE;
    554     }
    555     Patch *removedPatch = mPatches[index];
    556     mPatches.removeAt(index);
    557 
    558     struct audio_patch *patch = &removedPatch->mAudioPatch;
    559 
    560     switch (patch->sources[0].type) {
    561         case AUDIO_PORT_TYPE_DEVICE: {
    562             audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
    563             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    564             if (index < 0) {
    565                 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
    566                 status = BAD_VALUE;
    567                 break;
    568             }
    569 
    570             if (removedPatch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE ||
    571                     removedPatch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) {
    572                 clearPatchConnections(removedPatch);
    573                 break;
    574             }
    575 
    576             if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
    577                 sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
    578                                                                 patch->sinks[0].ext.mix.handle);
    579                 if (thread == 0) {
    580                     ALOGW("releaseAudioPatch() bad capture I/O handle %d",
    581                                                               patch->sinks[0].ext.mix.handle);
    582                     status = BAD_VALUE;
    583                     break;
    584                 }
    585                 status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
    586             } else {
    587                 AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    588                 if (audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) {
    589                     status = INVALID_OPERATION;
    590                     break;
    591                 }
    592                 audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    593                 status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle);
    594             }
    595         } break;
    596         case AUDIO_PORT_TYPE_MIX: {
    597             audio_module_handle_t srcModule =  patch->sources[0].ext.mix.hw_module;
    598             ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule);
    599             if (index < 0) {
    600                 ALOGW("releaseAudioPatch() bad src hw module %d", srcModule);
    601                 status = BAD_VALUE;
    602                 break;
    603             }
    604             sp<ThreadBase> thread =
    605                             audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle);
    606             if (thread == 0) {
    607                 ALOGW("releaseAudioPatch() bad playback I/O handle %d",
    608                                                               patch->sources[0].ext.mix.handle);
    609                 status = BAD_VALUE;
    610                 break;
    611             }
    612             status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle);
    613         } break;
    614         default:
    615             status = BAD_VALUE;
    616             break;
    617     }
    618 
    619     delete removedPatch;
    620     return status;
    621 }
    622 
    623 
    624 /* List connected audio ports and they attributes */
    625 status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused,
    626                                   struct audio_patch *patches __unused)
    627 {
    628     ALOGV("listAudioPatches");
    629     return NO_ERROR;
    630 }
    631 
    632 /* Set audio port configuration */
    633 status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
    634 {
    635     ALOGV("setAudioPortConfig");
    636 
    637     sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
    638     if (audioflinger == 0) {
    639         return NO_INIT;
    640     }
    641 
    642     audio_module_handle_t module;
    643     if (config->type == AUDIO_PORT_TYPE_DEVICE) {
    644         module = config->ext.device.hw_module;
    645     } else {
    646         module = config->ext.mix.hw_module;
    647     }
    648 
    649     ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
    650     if (index < 0) {
    651         ALOGW("setAudioPortConfig() bad hw module %d", module);
    652         return BAD_VALUE;
    653     }
    654 
    655     AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
    656     if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
    657         audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
    658         return hwDevice->set_audio_port_config(hwDevice, config);
    659     } else {
    660         return INVALID_OPERATION;
    661     }
    662     return NO_ERROR;
    663 }
    664 
    665 } // namespace android
    666