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