/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** Data locator, data format, data source, and data sink support */

#include "sles_allinclusive.h"


/** \brief Check a data locator and make local deep copy */

static SLresult checkDataLocator(const char *name, void *pLocator, DataLocator *pDataLocator,
        SLuint32 allowedDataLocatorMask)
{
    assert(NULL != name && NULL != pDataLocator);
    SLresult result = SL_RESULT_SUCCESS;

    SLuint32 locatorType;
    if (NULL == pLocator) {
        pDataLocator->mLocatorType = locatorType = SL_DATALOCATOR_NULL;
    } else {
        locatorType = *(SLuint32 *)pLocator;
        switch (locatorType) {

        case SL_DATALOCATOR_ADDRESS:
            pDataLocator->mAddress = *(SLDataLocator_Address *)pLocator;
            // if length is greater than zero, then the address must be non-NULL
            if ((0 < pDataLocator->mAddress.length) && (NULL == pDataLocator->mAddress.pAddress)) {
                SL_LOGE("%s: pAddress=NULL", name);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;

        case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
        // This is an alias that is _not_ converted; the rest of the code must check for both
        // locator types. That's because it is only an alias for audio players, not audio recorder
        // objects so we have to remember the distinction.
        case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
            pDataLocator->mBufferQueue = *(SLDataLocator_BufferQueue *)pLocator;
            // number of buffers must be specified, there is no default value, and can't be too big
            if (!((1 <= pDataLocator->mBufferQueue.numBuffers) &&
                (pDataLocator->mBufferQueue.numBuffers <= 255))) {
                SL_LOGE("%s: numBuffers=%u", name, pDataLocator->mBufferQueue.numBuffers);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;

        case SL_DATALOCATOR_IODEVICE:
            {
            pDataLocator->mIODevice = *(SLDataLocator_IODevice *)pLocator;
            SLuint32 deviceType = pDataLocator->mIODevice.deviceType;
            SLObjectItf device = pDataLocator->mIODevice.device;
            if (NULL != device) {
                pDataLocator->mIODevice.deviceID = 0;
                SLuint32 expectedObjectID;
                switch (deviceType) {
                case SL_IODEVICE_LEDARRAY:
                    expectedObjectID = SL_OBJECTID_LEDDEVICE;
                    break;
                case SL_IODEVICE_VIBRA:
                    expectedObjectID = SL_OBJECTID_VIBRADEVICE;
                    break;
                case XA_IODEVICE_CAMERA:
                    expectedObjectID = XA_OBJECTID_CAMERADEVICE;
                    break;
                case XA_IODEVICE_RADIO:
                    expectedObjectID = XA_OBJECTID_RADIODEVICE;
                    break;
                // audio input and audio output cannot be specified via objects
                case SL_IODEVICE_AUDIOINPUT:
                // case SL_IODEVICE_AUDIOOUTPUT:   // does not exist in 1.0.1, added in 1.1
                default:
                    SL_LOGE("%s: deviceType=%u", name, deviceType);
                    pDataLocator->mIODevice.device = NULL;
                    expectedObjectID = 0;
                    result = SL_RESULT_PARAMETER_INVALID;
                }
                if (result == SL_RESULT_SUCCESS) {
                    // check that device has the correct object ID and is realized,
                    // and acquire a strong reference to it
                    result = AcquireStrongRef((IObject *) device, expectedObjectID);
                    if (SL_RESULT_SUCCESS != result) {
                        SL_LOGE("%s: locatorType=IODEVICE, but device field %p has wrong " \
                            "object ID or is not realized", name, device);
                        pDataLocator->mIODevice.device = NULL;
                    }
                }
            } else {
                SLuint32 deviceID = pDataLocator->mIODevice.deviceID;
                switch (deviceType) {
                case SL_IODEVICE_LEDARRAY:
                    if (SL_DEFAULTDEVICEID_LED != deviceID) {
                        SL_LOGE("%s: invalid LED deviceID=%u", name, deviceID);
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                    break;
                case SL_IODEVICE_VIBRA:
                    if (SL_DEFAULTDEVICEID_VIBRA != deviceID) {
                        SL_LOGE("%s: invalid vibra deviceID=%u", name, deviceID);
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                    break;
                case SL_IODEVICE_AUDIOINPUT:
                    if (SL_DEFAULTDEVICEID_AUDIOINPUT != deviceID) {
                        SL_LOGE("%s: invalid audio input deviceID=%u", name, deviceID);
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                    break;
                case XA_IODEVICE_RADIO:
                    // no default device ID for radio; see Khronos bug XXXX
                    break;
                case XA_IODEVICE_CAMERA:
                    if (XA_DEFAULTDEVICEID_CAMERA != deviceID) {
                        SL_LOGE("%s: invalid audio input deviceID=%u", name, deviceID);
                        result = XA_RESULT_PARAMETER_INVALID;
                    }
                    break;
                // case SL_IODEVICE_AUDIOOUTPUT:
                    // does not exist in 1.0.1, added in 1.1
                    // break;
                default:
                    SL_LOGE("%s: deviceType=%u is invalid", name, deviceType);
                    result = SL_RESULT_PARAMETER_INVALID;
                }
            }
            }
            break;

        case SL_DATALOCATOR_MIDIBUFFERQUEUE:
            pDataLocator->mMIDIBufferQueue = *(SLDataLocator_MIDIBufferQueue *)pLocator;
            if (0 == pDataLocator->mMIDIBufferQueue.tpqn) {
                pDataLocator->mMIDIBufferQueue.tpqn = 192;
            }
            // number of buffers must be specified, there is no default value, and can't be too big
            if (!((1 <= pDataLocator->mMIDIBufferQueue.numBuffers) &&
                (pDataLocator->mMIDIBufferQueue.numBuffers <= 255))) {
                SL_LOGE("%s: SLDataLocator_MIDIBufferQueue.numBuffers=%d", name,
                        pDataLocator->mMIDIBufferQueue.numBuffers);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;

        case SL_DATALOCATOR_OUTPUTMIX:
            pDataLocator->mOutputMix = *(SLDataLocator_OutputMix *)pLocator;
            // check that output mix object has the correct object ID and is realized,
            // and acquire a strong reference to it
            result = AcquireStrongRef((IObject *) pDataLocator->mOutputMix.outputMix,
                SL_OBJECTID_OUTPUTMIX);
            if (SL_RESULT_SUCCESS != result) {
                SL_LOGE("%s: locatorType=SL_DATALOCATOR_OUTPUTMIX, but outputMix field %p does " \
                    "not refer to an SL_OBJECTID_OUTPUTMIX or the output mix is not realized", \
                    name, pDataLocator->mOutputMix.outputMix);
                pDataLocator->mOutputMix.outputMix = NULL;
            }
            break;

        case XA_DATALOCATOR_NATIVEDISPLAY:
            pDataLocator->mNativeDisplay = *(XADataLocator_NativeDisplay *)pLocator;
            // hWindow is NDK C ANativeWindow * and hDisplay must be NULL
            if (pDataLocator->mNativeDisplay.hWindow == NULL) {
                SL_LOGE("%s: hWindow must be non-NULL ANativeWindow *", name);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            if (pDataLocator->mNativeDisplay.hDisplay != NULL) {
                SL_LOGE("%s: hDisplay must be NULL, but is %p", name,
                        pDataLocator->mNativeDisplay.hDisplay);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;

        case SL_DATALOCATOR_URI:
            {
            pDataLocator->mURI = *(SLDataLocator_URI *)pLocator;
            if (NULL == pDataLocator->mURI.URI) {
                SL_LOGE("%s: invalid URI=NULL", name);
                result = SL_RESULT_PARAMETER_INVALID;
            } else {
                // NTH verify URI address for validity
                size_t len = strlen((const char *) pDataLocator->mURI.URI);
                SLchar *myURI = (SLchar *) malloc(len + 1);
                if (NULL == myURI) {
                    result = SL_RESULT_MEMORY_FAILURE;
                } else {
                    memcpy(myURI, pDataLocator->mURI.URI, len + 1);
                    // Verify that another thread didn't change the NUL-terminator after we used it
                    // to determine length of string to copy. It's OK if the string became shorter.
                    if ('\0' != myURI[len]) {
                        free(myURI);
                        myURI = NULL;
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                }
                pDataLocator->mURI.URI = myURI;
            }
            }
            break;

#ifdef ANDROID
        case SL_DATALOCATOR_ANDROIDFD:
        {
            pDataLocator->mFD = *(SLDataLocator_AndroidFD *)pLocator;
            SL_LOGV("%s: fd=%d offset=%lld length=%lld", name, pDataLocator->mFD.fd,
                    pDataLocator->mFD.offset, pDataLocator->mFD.length);
            // NTH check against process fd limit
            if (0 > pDataLocator->mFD.fd) {
                SL_LOGE("%s: fd=%d\n", name, pDataLocator->mFD.fd);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;
        }
        case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
        {
            pDataLocator->mABQ = *(SLDataLocator_AndroidBufferQueue*)pLocator;
            // number of buffers must be specified, there is no default value, and can't be too big
            if (!((1 <= pDataLocator->mBufferQueue.numBuffers) &&
                    (pDataLocator->mBufferQueue.numBuffers <= 255))) {
                SL_LOGE("%s: numBuffers=%u", name, pDataLocator->mABQ.numBuffers);
                result = SL_RESULT_PARAMETER_INVALID;
            }
            break;
        }
#endif

        case SL_DATALOCATOR_NULL:   // a NULL pointer is allowed, but not a pointer to NULL
        default:
            SL_LOGE("%s: locatorType=%u", name, locatorType);
            result = SL_RESULT_PARAMETER_INVALID;
        }

        // Verify that another thread didn't change the locatorType field after we used it
        // to determine sizeof struct to copy.
        if ((SL_RESULT_SUCCESS == result) && (locatorType != pDataLocator->mLocatorType)) {
            SL_LOGE("%s: locatorType changed from %u to %u", name, locatorType,
                    pDataLocator->mLocatorType);
            result = SL_RESULT_PRECONDITIONS_VIOLATED;
        }

    }

    // Verify that the data locator type is allowed in this context
    if (SL_RESULT_SUCCESS == result) {
        SLuint32 actualMask;
        switch (locatorType) {
        case SL_DATALOCATOR_NULL:
        case SL_DATALOCATOR_URI:
        case SL_DATALOCATOR_ADDRESS:
        case SL_DATALOCATOR_IODEVICE:
        case SL_DATALOCATOR_OUTPUTMIX:
        case XA_DATALOCATOR_NATIVEDISPLAY:
        case SL_DATALOCATOR_BUFFERQUEUE:
        case SL_DATALOCATOR_MIDIBUFFERQUEUE:
            actualMask = 1L << locatorType;
            break;
#ifdef ANDROID
        case SL_DATALOCATOR_ANDROIDFD:
        case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
        case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
            actualMask = 0x100L << (locatorType - SL_DATALOCATOR_ANDROIDFD);
            break;
#endif
        default:
            assert(false);
            actualMask = 0L;
            break;
        }
        if (!(allowedDataLocatorMask & actualMask)) {
            SL_LOGE("%s: data locator type 0x%x not allowed", name, locatorType);
            result = SL_RESULT_CONTENT_UNSUPPORTED;
        }
    }

    return result;
}


/** \brief Free the local deep copy of a data locator */

static void freeDataLocator(DataLocator *pDataLocator)
{
    switch (pDataLocator->mLocatorType) {
    case SL_DATALOCATOR_NULL:
    case SL_DATALOCATOR_ADDRESS:
    case SL_DATALOCATOR_BUFFERQUEUE:
    case SL_DATALOCATOR_MIDIBUFFERQUEUE:
    case XA_DATALOCATOR_NATIVEDISPLAY:
        break;
    case SL_DATALOCATOR_URI:
        if (NULL != pDataLocator->mURI.URI) {
            free(pDataLocator->mURI.URI);
            pDataLocator->mURI.URI = NULL;
        }
        pDataLocator->mURI.URI = NULL;
        break;
    case SL_DATALOCATOR_IODEVICE:
        if (NULL != pDataLocator->mIODevice.device) {
            ReleaseStrongRef((IObject *) pDataLocator->mIODevice.device);
            pDataLocator->mIODevice.device = NULL;
        }
        break;
    case SL_DATALOCATOR_OUTPUTMIX:
        if (NULL != pDataLocator->mOutputMix.outputMix) {
            ReleaseStrongRef((IObject *) pDataLocator->mOutputMix.outputMix);
            pDataLocator->mOutputMix.outputMix = NULL;
        }
        break;
#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
    case SL_DATALOCATOR_ANDROIDFD:
        break;
#endif
    default:
        // an invalid data locator is caught earlier when making the copy
        assert(false);
        break;
    }
}


/** \brief Check a data format and make local deep copy */

static SLresult checkDataFormat(const char *name, void *pFormat, DataFormat *pDataFormat,
        SLuint32 allowedDataFormatMask)
{
    assert(NULL != name && NULL != pDataFormat);
    SLresult result = SL_RESULT_SUCCESS;

    SLuint32 formatType;
    if (NULL == pFormat) {
        pDataFormat->mFormatType = formatType = SL_DATAFORMAT_NULL;
    } else {
        formatType = *(SLuint32 *)pFormat;
        switch (formatType) {

        case SL_DATAFORMAT_PCM:
            pDataFormat->mPCM = *(SLDataFormat_PCM *)pFormat;
            do {

                // check the channel count
                switch (pDataFormat->mPCM.numChannels) {
                case 1:     // mono
                case 2:     // stereo
                    break;
                case 0:     // unknown
                    result = SL_RESULT_PARAMETER_INVALID;
                    break;
                default:    // multi-channel
                    result = SL_RESULT_CONTENT_UNSUPPORTED;
                    break;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: numChannels=%u", name, (unsigned) pDataFormat->mPCM.numChannels);
                    break;
                }

                // check the sampling rate
                switch (pDataFormat->mPCM.samplesPerSec) {
                case SL_SAMPLINGRATE_8:
                case SL_SAMPLINGRATE_11_025:
                case SL_SAMPLINGRATE_12:
                case SL_SAMPLINGRATE_16:
                case SL_SAMPLINGRATE_22_05:
                case SL_SAMPLINGRATE_24:
                case SL_SAMPLINGRATE_32:
                case SL_SAMPLINGRATE_44_1:
                case SL_SAMPLINGRATE_48:
                case SL_SAMPLINGRATE_64:
                case SL_SAMPLINGRATE_88_2:
                case SL_SAMPLINGRATE_96:
                case SL_SAMPLINGRATE_192:
                    break;
                case 0:
                    result = SL_RESULT_PARAMETER_INVALID;
                    break;
                default:
                    result = SL_RESULT_CONTENT_UNSUPPORTED;
                    break;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: samplesPerSec=%u", name, pDataFormat->mPCM.samplesPerSec);
                    break;
                }

                // check the sample bit depth
                switch (pDataFormat->mPCM.bitsPerSample) {
                case SL_PCMSAMPLEFORMAT_FIXED_8:
                case SL_PCMSAMPLEFORMAT_FIXED_16:
                    break;
                case SL_PCMSAMPLEFORMAT_FIXED_20:
                case SL_PCMSAMPLEFORMAT_FIXED_24:
                case SL_PCMSAMPLEFORMAT_FIXED_28:
                case SL_PCMSAMPLEFORMAT_FIXED_32:
                    result = SL_RESULT_CONTENT_UNSUPPORTED;
                    break;
                default:
                    result = SL_RESULT_PARAMETER_INVALID;
                    break;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: bitsPerSample=%u", name, pDataFormat->mPCM.bitsPerSample);
                    break;
                }

                // check the container bit depth
                if (pDataFormat->mPCM.containerSize < pDataFormat->mPCM.bitsPerSample) {
                    result = SL_RESULT_PARAMETER_INVALID;
                } else if (pDataFormat->mPCM.containerSize != pDataFormat->mPCM.bitsPerSample) {
                    result = SL_RESULT_CONTENT_UNSUPPORTED;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: containerSize=%u, bitsPerSample=%u", name,
                            (unsigned) pDataFormat->mPCM.containerSize,
                            (unsigned) pDataFormat->mPCM.bitsPerSample);
                    break;
                }

                // check the channel mask
                switch (pDataFormat->mPCM.channelMask) {
                case SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT:
                    if (2 != pDataFormat->mPCM.numChannels) {
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                    break;
                case SL_SPEAKER_FRONT_LEFT:
                case SL_SPEAKER_FRONT_RIGHT:
                case SL_SPEAKER_FRONT_CENTER:
                    if (1 != pDataFormat->mPCM.numChannels) {
                        result = SL_RESULT_PARAMETER_INVALID;
                    }
                    break;
                case 0:
                    // The default of front left rather than center for mono may be non-intuitive,
                    // but the left channel is the first channel for stereo or multichannel content.
                    pDataFormat->mPCM.channelMask = pDataFormat->mPCM.numChannels == 2 ?
                        SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT : SL_SPEAKER_FRONT_LEFT;
                    break;
                default:
                    result = SL_RESULT_PARAMETER_INVALID;
                    break;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: channelMask=0x%x numChannels=%u", name,
                        pDataFormat->mPCM.channelMask, pDataFormat->mPCM.numChannels);
                    break;
                }

                // check the endianness / byte order
                switch (pDataFormat->mPCM.endianness) {
                case SL_BYTEORDER_LITTLEENDIAN:
                case SL_BYTEORDER_BIGENDIAN:
                    break;
                // native is proposed but not yet in spec
                default:
                    result = SL_RESULT_PARAMETER_INVALID;
                    break;
                }
                if (SL_RESULT_SUCCESS != result) {
                    SL_LOGE("%s: endianness=%u", name, (unsigned) pDataFormat->mPCM.endianness);
                    break;
                }

                // here if all checks passed successfully

            } while(0);
            break;

        case SL_DATAFORMAT_MIME:
            pDataFormat->mMIME = *(SLDataFormat_MIME *)pFormat;
            if (NULL != pDataFormat->mMIME.mimeType) {
                // NTH check address for validity
                size_t len = strlen((const char *) pDataFormat->mMIME.mimeType);
                SLchar *myMIME = (SLchar *) malloc(len + 1);
                if (NULL == myMIME) {
                    result = SL_RESULT_MEMORY_FAILURE;
                } else {
                    memcpy(myMIME, pDataFormat->mMIME.mimeType, len + 1);
                    // make sure MIME string was not modified asynchronously
                    if ('\0' != myMIME[len]) {
                        free(myMIME);
                        myMIME = NULL;
                        result = SL_RESULT_PRECONDITIONS_VIOLATED;
                    }
                }
                pDataFormat->mMIME.mimeType = myMIME;
            }
            break;

        case XA_DATAFORMAT_RAWIMAGE:
            pDataFormat->mRawImage = *(XADataFormat_RawImage *)pFormat;
            switch (pDataFormat->mRawImage.colorFormat) {
            case XA_COLORFORMAT_MONOCHROME:
            case XA_COLORFORMAT_8BITRGB332:
            case XA_COLORFORMAT_12BITRGB444:
            case XA_COLORFORMAT_16BITARGB4444:
            case XA_COLORFORMAT_16BITARGB1555:
            case XA_COLORFORMAT_16BITRGB565:
            case XA_COLORFORMAT_16BITBGR565:
            case XA_COLORFORMAT_18BITRGB666:
            case XA_COLORFORMAT_18BITARGB1665:
            case XA_COLORFORMAT_19BITARGB1666:
            case XA_COLORFORMAT_24BITRGB888:
            case XA_COLORFORMAT_24BITBGR888:
            case XA_COLORFORMAT_24BITARGB1887:
            case XA_COLORFORMAT_25BITARGB1888:
            case XA_COLORFORMAT_32BITBGRA8888:
            case XA_COLORFORMAT_32BITARGB8888:
            case XA_COLORFORMAT_YUV411PLANAR:
            case XA_COLORFORMAT_YUV420PLANAR:
            case XA_COLORFORMAT_YUV420SEMIPLANAR:
            case XA_COLORFORMAT_YUV422PLANAR:
            case XA_COLORFORMAT_YUV422SEMIPLANAR:
            case XA_COLORFORMAT_YCBYCR:
            case XA_COLORFORMAT_YCRYCB:
            case XA_COLORFORMAT_CBYCRY:
            case XA_COLORFORMAT_CRYCBY:
            case XA_COLORFORMAT_YUV444INTERLEAVED:
            case XA_COLORFORMAT_RAWBAYER8BIT:
            case XA_COLORFORMAT_RAWBAYER10BIT:
            case XA_COLORFORMAT_RAWBAYER8BITCOMPRESSED:
            case XA_COLORFORMAT_L2:
            case XA_COLORFORMAT_L4:
            case XA_COLORFORMAT_L8:
            case XA_COLORFORMAT_L16:
            case XA_COLORFORMAT_L24:
            case XA_COLORFORMAT_L32:
            case XA_COLORFORMAT_18BITBGR666:
            case XA_COLORFORMAT_24BITARGB6666:
            case XA_COLORFORMAT_24BITABGR6666:
                break;
            case XA_COLORFORMAT_UNUSED:
            default:
                result = XA_RESULT_PARAMETER_INVALID;
                SL_LOGE("%s: unsupported color format %d", name,
                    pDataFormat->mRawImage.colorFormat);
                break;
            }
            // no checks for height, width, or stride
            break;

        default:
            result = SL_RESULT_PARAMETER_INVALID;
            SL_LOGE("%s: formatType=%u", name, (unsigned) formatType);
            break;

        }

        // make sure format type was not modified asynchronously
        if ((SL_RESULT_SUCCESS == result) && (formatType != pDataFormat->mFormatType)) {
            SL_LOGE("%s: formatType changed from %u to %u", name, formatType,
                    pDataFormat->mFormatType);
            result = SL_RESULT_PRECONDITIONS_VIOLATED;
        }

    }

    // Verify that the data format type is allowed in this context
    if (SL_RESULT_SUCCESS == result) {
        SLuint32 actualMask;
        switch (formatType) {
        case SL_DATAFORMAT_NULL:
        case SL_DATAFORMAT_MIME:
        case SL_DATAFORMAT_PCM:
        case XA_DATAFORMAT_RAWIMAGE:
            actualMask = 1L << formatType;
            break;
        default:
            assert(false);
            actualMask = 0L;
            break;
        }
        if (!(allowedDataFormatMask & actualMask)) {
            SL_LOGE("%s: data format %d not allowed", name, formatType);
            result = SL_RESULT_CONTENT_UNSUPPORTED;
        }
    }

    return result;
}


/** \brief Check interface ID compatibility with respect to a particular source
 *         and sink data locator format
 */

SLresult checkSourceSinkVsInterfacesCompatibility(const DataLocatorFormat *pSrcDataLocatorFormat,
        const DataLocatorFormat *pSinkDataLocatorFormat,
        const ClassTable *clazz, unsigned exposedMask) {
    int index;
    switch (pSrcDataLocatorFormat->mLocator.mLocatorType) {
    case SL_DATALOCATOR_URI:
#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDFD:
#endif
        // URIs and FD can be sources when "playing" to an OutputMix or a Buffer Queue for decode
        // so we don't prevent the retrieval of the BufferQueue interfaces for those sources
        switch (pSinkDataLocatorFormat->mLocator.mLocatorType) {
        case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
        case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
            break;
        default:
            // can't request SLBufferQueueItf or its alias SLAndroidSimpleBufferQueueItf
            // if the data sink is not a buffer queue
            index = clazz->mMPH_to_index[MPH_BUFFERQUEUE];
#ifdef ANDROID
            assert(index == clazz->mMPH_to_index[MPH_ANDROIDSIMPLEBUFFERQUEUE]);
#endif
            if (0 <= index) {
                if (exposedMask & (1 << index)) {
                    SL_LOGE("can't request SL_IID_BUFFERQUEUE "
#ifdef ANDROID
                            "or SL_IID_ANDROIDSIMPLEBUFFERQUEUE "
#endif
                            "with a non-buffer queue data sink");
                    return SL_RESULT_FEATURE_UNSUPPORTED;
                }
            }
            break;
        }
        break;

    case SL_DATALOCATOR_BUFFERQUEUE:
#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
#endif
        // can't request SLSeekItf if data source is a buffer queue
        index = clazz->mMPH_to_index[MPH_SEEK];
        if (0 <= index) {
            if (exposedMask & (1 << index)) {
                SL_LOGE("can't request SL_IID_SEEK with a buffer queue data source");
                return SL_RESULT_FEATURE_UNSUPPORTED;
            }
        }
        // can't request SLMuteSoloItf if data source is a mono buffer queue
        index = clazz->mMPH_to_index[MPH_MUTESOLO];
        if (0 <= index) {
            if ((exposedMask & (1 << index)) &&
                    (SL_DATAFORMAT_PCM == pSrcDataLocatorFormat->mFormat.mFormatType) &&
                    (1 == pSrcDataLocatorFormat->mFormat.mPCM.numChannels)) {
                SL_LOGE("can't request SL_IID_MUTESOLO with a mono buffer queue data source");
                return SL_RESULT_FEATURE_UNSUPPORTED;
            }
        }
        break;

#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
        // if the source is SLAndroidBufferQueueItf for AAC decode, the sink must be a buffer queue
        switch (pSinkDataLocatorFormat->mLocator.mLocatorType) {
        case SL_DATALOCATOR_BUFFERQUEUE:
        case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
            break;
        default:
            // FIXME more mph index business to worry about?
            // FIXME does this break OpenMAX AL?
            SL_LOGE("Source is SL_DATALOCATOR_ANDROIDBUFFERQUEUE, sink is not a buffer queue");
            return SL_RESULT_FEATURE_UNSUPPORTED;
            break;
        }
        break;
#endif
    case SL_DATALOCATOR_ADDRESS:
    case SL_DATALOCATOR_MIDIBUFFERQUEUE:
    case XA_DATALOCATOR_NATIVEDISPLAY:
        // any special checks here???
    default:
        // can't request SLBufferQueueItf or its alias SLAndroidSimpleBufferQueueItf
        // if the data source is not a buffer queue
        index = clazz->mMPH_to_index[MPH_BUFFERQUEUE];
#ifdef ANDROID
        assert(index == clazz->mMPH_to_index[MPH_ANDROIDSIMPLEBUFFERQUEUE]);
#endif
        if (0 <= index) {
            if (exposedMask & (1 << index)) {
                SL_LOGE("can't request SL_IID_BUFFERQUEUE "
#ifdef ANDROID
                        "or SL_IID_ANDROIDSIMPLEBUFFERQUEUE "
#endif
                        "with a non-buffer queue data source");
                return SL_RESULT_FEATURE_UNSUPPORTED;
            }
        }
        break;
    }
    return SL_RESULT_SUCCESS;
}


/** \brief Free the local deep copy of a data format */

static void freeDataFormat(DataFormat *pDataFormat)
{
    switch (pDataFormat->mFormatType) {
    case SL_DATAFORMAT_MIME:
        if (NULL != pDataFormat->mMIME.mimeType) {
            free(pDataFormat->mMIME.mimeType);
            pDataFormat->mMIME.mimeType = NULL;
        }
        break;
    case SL_DATAFORMAT_PCM:
    case XA_DATAFORMAT_RAWIMAGE:
    case SL_DATAFORMAT_NULL:
        break;
    default:
        // an invalid data format is caught earlier during the copy
        assert(false);
        break;
    }
}


/** \brief Check a data source and make local deep copy */

SLresult checkDataSource(const char *name, const SLDataSource *pDataSrc,
        DataLocatorFormat *pDataLocatorFormat, SLuint32 allowedDataLocatorMask,
        SLuint32 allowedDataFormatMask)
{
    assert(NULL != name && NULL != pDataLocatorFormat);
    pDataLocatorFormat->u.mSource.pLocator = &pDataLocatorFormat->mLocator;
    pDataLocatorFormat->u.mSource.pFormat = &pDataLocatorFormat->mFormat;

    if (NULL == pDataSrc) {
        pDataLocatorFormat->mLocator.mLocatorType = SL_DATALOCATOR_NULL;
        pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL;
        if ((allowedDataLocatorMask & DATALOCATOR_MASK_NULL) &&
                (allowedDataFormatMask & DATAFORMAT_MASK_NULL)) {
            return SL_RESULT_SUCCESS;
        }
        SL_LOGE("%s: data source cannot be NULL", name);
        return SL_RESULT_PARAMETER_INVALID;
    }
    SLDataSource myDataSrc = *pDataSrc;
    SLresult result;
    result = checkDataLocator(name, myDataSrc.pLocator, &pDataLocatorFormat->mLocator,
            allowedDataLocatorMask);
    if (SL_RESULT_SUCCESS != result) {
        return result;
    }

    switch (pDataLocatorFormat->mLocator.mLocatorType) {
    case SL_DATALOCATOR_URI:
        allowedDataFormatMask &= DATAFORMAT_MASK_MIME;
        break;
    case SL_DATALOCATOR_ADDRESS:
    case SL_DATALOCATOR_BUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_PCM;
        break;
    // Per the spec, the pFormat field is ignored in some cases
    case SL_DATALOCATOR_IODEVICE:
        myDataSrc.pFormat = NULL;
        // fall through
    case SL_DATALOCATOR_NULL:
    case SL_DATALOCATOR_MIDIBUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_NULL;
        break;
    case SL_DATALOCATOR_OUTPUTMIX:
    case XA_DATALOCATOR_NATIVEDISPLAY:
        allowedDataFormatMask = DATAFORMAT_MASK_NONE;
        break;
#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDFD:
        allowedDataFormatMask &= DATAFORMAT_MASK_MIME;
        break;
    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_PCM;
        break;
    case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_MIME;;
        break;
#endif
    default:
        // invalid data locator type is caught earlier
        assert(false);
        allowedDataFormatMask = DATAFORMAT_MASK_NONE;
        break;
    }

    result = checkDataFormat(name, myDataSrc.pFormat, &pDataLocatorFormat->mFormat,
            allowedDataFormatMask);
    if (SL_RESULT_SUCCESS != result) {
        freeDataLocator(&pDataLocatorFormat->mLocator);
        return result;
    }

    return SL_RESULT_SUCCESS;
}


/** \brief Check a data sink and make local deep copy */

SLresult checkDataSink(const char *name, const SLDataSink *pDataSink,
        DataLocatorFormat *pDataLocatorFormat, SLuint32 allowedDataLocatorMask,
        SLuint32 allowedDataFormatMask)
{
    assert(NULL != name && NULL != pDataLocatorFormat);
    pDataLocatorFormat->u.mSink.pLocator = &pDataLocatorFormat->mLocator;
    pDataLocatorFormat->u.mSink.pFormat = &pDataLocatorFormat->mFormat;

    if (NULL == pDataSink) {
        pDataLocatorFormat->mLocator.mLocatorType = SL_DATALOCATOR_NULL;
        pDataLocatorFormat->mFormat.mFormatType = SL_DATAFORMAT_NULL;
        if ((allowedDataLocatorMask & DATALOCATOR_MASK_NULL) &&
                (allowedDataFormatMask & DATAFORMAT_MASK_NULL)) {
            return SL_RESULT_SUCCESS;
        }
        SL_LOGE("%s: data sink cannot be NULL", name);
        return SL_RESULT_PARAMETER_INVALID;
    }
    SLDataSink myDataSink = *pDataSink;
    SLresult result;
    result = checkDataLocator(name, myDataSink.pLocator, &pDataLocatorFormat->mLocator,
            allowedDataLocatorMask);
    if (SL_RESULT_SUCCESS != result) {
        return result;
    }

    switch (pDataLocatorFormat->mLocator.mLocatorType) {
    case SL_DATALOCATOR_URI:
        allowedDataFormatMask &= DATAFORMAT_MASK_MIME;
        break;
    case SL_DATALOCATOR_ADDRESS:
    case SL_DATALOCATOR_BUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_PCM;
        break;
    // Per the spec, the pFormat field is ignored in some cases
    case SL_DATALOCATOR_IODEVICE:
    case SL_DATALOCATOR_OUTPUTMIX:
    case XA_DATALOCATOR_NATIVEDISPLAY:
        myDataSink.pFormat = NULL;
        // fall through
    case SL_DATALOCATOR_NULL:
    case SL_DATALOCATOR_MIDIBUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_NULL;
        break;
#ifdef ANDROID
    case SL_DATALOCATOR_ANDROIDFD:
        allowedDataFormatMask = DATAFORMAT_MASK_NONE;
        break;
    case SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE:
        allowedDataFormatMask &= DATAFORMAT_MASK_PCM;
        break;
    case SL_DATALOCATOR_ANDROIDBUFFERQUEUE:
        allowedDataFormatMask = DATAFORMAT_MASK_NONE;
        break;
#endif
    default:
        // invalid data locator type is caught earlier
        assert(false);
        allowedDataFormatMask = DATAFORMAT_MASK_NONE;
        break;
    }

    result = checkDataFormat(name, myDataSink.pFormat, &pDataLocatorFormat->mFormat,
            allowedDataFormatMask);
    if (SL_RESULT_SUCCESS != result) {
        freeDataLocator(&pDataLocatorFormat->mLocator);
        return result;
    }

    return SL_RESULT_SUCCESS;
}


/** \brief Free the local deep copy of a data locator format */

void freeDataLocatorFormat(DataLocatorFormat *dlf)
{
    assert(NULL != dlf);
    freeDataLocator(&dlf->mLocator);
    freeDataFormat(&dlf->mFormat);
}
