/*
 * Copyright 2016 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.
 */

#define LOG_TAG "AAudio"
//#define LOG_NDEBUG 0

#include <assert.h>
#include <math.h>
#include <stdint.h>

#include <aaudio/AAudioTesting.h>
#include <android/media/audio/common/AudioMMapPolicy.h>
#include <cutils/properties.h>
#include <sys/types.h>
#include <system/audio.h>
#include <utils/Errors.h>
#include <utils/Log.h>

#include "aaudio/AAudio.h"
#include "core/AudioGlobal.h"
#include "utility/AAudioUtilities.h"

using namespace android;

using android::media::audio::common::AudioMMapPolicy;
using android::media::audio::common::AudioMMapPolicyInfo;

status_t AAudioConvert_aaudioToAndroidStatus(aaudio_result_t result) {
    // This covers the case for AAUDIO_OK and for positive results.
    if (result >= 0) {
        return result;
    }
    status_t status;
    switch (result) {
    case AAUDIO_ERROR_DISCONNECTED:
    case AAUDIO_ERROR_NO_SERVICE:
        status = DEAD_OBJECT;
        break;
    case AAUDIO_ERROR_INVALID_HANDLE:
        status = BAD_TYPE;
        break;
    case AAUDIO_ERROR_INVALID_STATE:
        status = INVALID_OPERATION;
        break;
    case AAUDIO_ERROR_INVALID_RATE:
    case AAUDIO_ERROR_INVALID_FORMAT:
    case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
    case AAUDIO_ERROR_OUT_OF_RANGE:
        status = BAD_VALUE;
        break;
    case AAUDIO_ERROR_WOULD_BLOCK:
        status = WOULD_BLOCK;
        break;
    case AAUDIO_ERROR_NULL:
        status = UNEXPECTED_NULL;
        break;
    case AAUDIO_ERROR_UNAVAILABLE:
        status = NOT_ENOUGH_DATA;
        break;

    // TODO translate these result codes
    case AAUDIO_ERROR_INTERNAL:
    case AAUDIO_ERROR_UNIMPLEMENTED:
    case AAUDIO_ERROR_NO_FREE_HANDLES:
    case AAUDIO_ERROR_NO_MEMORY:
    case AAUDIO_ERROR_TIMEOUT:
    default:
        status = UNKNOWN_ERROR;
        break;
    }
    return status;
}

aaudio_result_t AAudioConvert_androidToAAudioResult(status_t status) {
    // This covers the case for OK and for positive result.
    if (status >= 0) {
        return status;
    }
    aaudio_result_t result;
    switch (status) {
    case BAD_TYPE:
        result = AAUDIO_ERROR_INVALID_HANDLE;
        break;
    case DEAD_OBJECT:
        result = AAUDIO_ERROR_NO_SERVICE;
        break;
    case INVALID_OPERATION:
        result = AAUDIO_ERROR_INVALID_STATE;
        break;
    case UNEXPECTED_NULL:
        result = AAUDIO_ERROR_NULL;
        break;
    case BAD_VALUE:
        result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
        break;
    case WOULD_BLOCK:
        result = AAUDIO_ERROR_WOULD_BLOCK;
        break;
    case NOT_ENOUGH_DATA:
        result = AAUDIO_ERROR_UNAVAILABLE;
        break;
    default:
        result = AAUDIO_ERROR_INTERNAL;
        break;
    }
    return result;
}

audio_session_t AAudioConvert_aaudioToAndroidSessionId(aaudio_session_id_t sessionId) {
    // If not a regular sessionId then convert to a safe value of AUDIO_SESSION_ALLOCATE.
    return (sessionId == AAUDIO_SESSION_ID_ALLOCATE || sessionId == AAUDIO_SESSION_ID_NONE)
           ? AUDIO_SESSION_ALLOCATE
           : (audio_session_t) sessionId;
}

audio_format_t AAudioConvert_aaudioToAndroidDataFormat(aaudio_format_t aaudioFormat) {
    audio_format_t androidFormat;
    switch (aaudioFormat) {
    case AAUDIO_FORMAT_UNSPECIFIED:
        androidFormat = AUDIO_FORMAT_DEFAULT;
        break;
    case AAUDIO_FORMAT_PCM_I16:
        androidFormat = AUDIO_FORMAT_PCM_16_BIT;
        break;
    case AAUDIO_FORMAT_PCM_FLOAT:
        androidFormat = AUDIO_FORMAT_PCM_FLOAT;
        break;
    case AAUDIO_FORMAT_PCM_I24_PACKED:
        androidFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED;
        break;
    case AAUDIO_FORMAT_PCM_I32:
        androidFormat = AUDIO_FORMAT_PCM_32_BIT;
        break;
    case AAUDIO_FORMAT_IEC61937:
        androidFormat = AUDIO_FORMAT_IEC61937;
        break;
    case AAUDIO_FORMAT_MP3:
        androidFormat = AUDIO_FORMAT_MP3;
        break;
    case AAUDIO_FORMAT_AAC_LC:
        androidFormat = AUDIO_FORMAT_AAC_LC;
        break;
    case AAUDIO_FORMAT_AAC_HE_V1:
        androidFormat = AUDIO_FORMAT_AAC_HE_V1;
        break;
    case AAUDIO_FORMAT_AAC_HE_V2:
        androidFormat = AUDIO_FORMAT_AAC_HE_V2;
        break;
    case AAUDIO_FORMAT_AAC_ELD:
        androidFormat = AUDIO_FORMAT_AAC_ELD;
        break;
    case AAUDIO_FORMAT_AAC_XHE:
        androidFormat = AUDIO_FORMAT_AAC_XHE;
        break;
    case AAUDIO_FORMAT_OPUS:
        androidFormat = AUDIO_FORMAT_OPUS;
        break;
    default:
        androidFormat = AUDIO_FORMAT_INVALID;
        ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
        break;
    }
    return androidFormat;
}

aaudio_format_t AAudioConvert_androidToAAudioDataFormat(audio_format_t androidFormat) {
    aaudio_format_t aaudioFormat;
    switch (androidFormat) {
    case AUDIO_FORMAT_DEFAULT:
        aaudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
        break;
    case AUDIO_FORMAT_PCM_16_BIT:
        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
        break;
    case AUDIO_FORMAT_PCM_FLOAT:
        aaudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
        break;
    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
        aaudioFormat = AAUDIO_FORMAT_PCM_I24_PACKED;
        break;
    case AUDIO_FORMAT_PCM_32_BIT:
        aaudioFormat = AAUDIO_FORMAT_PCM_I32;
        break;
    case AUDIO_FORMAT_IEC61937:
        aaudioFormat = AAUDIO_FORMAT_IEC61937;
        break;
    case AUDIO_FORMAT_MP3:
        aaudioFormat = AAUDIO_FORMAT_MP3;
        break;
    case AUDIO_FORMAT_AAC_LC:
        aaudioFormat = AAUDIO_FORMAT_AAC_LC;
        break;
    case AUDIO_FORMAT_AAC_HE_V1:
        aaudioFormat = AAUDIO_FORMAT_AAC_HE_V1;
        break;
    case AUDIO_FORMAT_AAC_HE_V2:
        aaudioFormat = AAUDIO_FORMAT_AAC_HE_V2;
        break;
    case AUDIO_FORMAT_AAC_ELD:
        aaudioFormat = AAUDIO_FORMAT_AAC_ELD;
        break;
    case AUDIO_FORMAT_AAC_XHE:
        aaudioFormat = AAUDIO_FORMAT_AAC_XHE;
        break;
    case AUDIO_FORMAT_OPUS:
        aaudioFormat = AAUDIO_FORMAT_OPUS;
        break;
    default:
        aaudioFormat = AAUDIO_FORMAT_INVALID;
        ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
        break;
    }
    return aaudioFormat;
}

aaudio_format_t AAudioConvert_androidToNearestAAudioDataFormat(audio_format_t androidFormat) {
    // Special case AUDIO_FORMAT_PCM_8_24_BIT because this function should be used to find the
    // resolution of the data format. Setting AUDIO_FORMAT_PCM_8_24_BIT directly is not available
    // from AAudio but hardware may use AUDIO_FORMAT_PCM_8_24_BIT under the hood.
    if (androidFormat == AUDIO_FORMAT_PCM_8_24_BIT) {
        ALOGD("%s() converting 8.24 to 24 bit packed", __func__);
        return AAUDIO_FORMAT_PCM_I24_PACKED;
    }
    return AAudioConvert_androidToAAudioDataFormat(androidFormat);
}

// Make a message string from the condition.
#define STATIC_ASSERT(condition) static_assert(condition, #condition)

audio_usage_t AAudioConvert_usageToInternal(aaudio_usage_t usage) {
    // The public aaudio_content_type_t constants are supposed to have the same
    // values as the internal audio_content_type_t values.
    STATIC_ASSERT(AAUDIO_USAGE_MEDIA == AUDIO_USAGE_MEDIA);
    STATIC_ASSERT(AAUDIO_USAGE_VOICE_COMMUNICATION == AUDIO_USAGE_VOICE_COMMUNICATION);
    STATIC_ASSERT(AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING
                  == AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING);
    STATIC_ASSERT(AAUDIO_USAGE_ALARM == AUDIO_USAGE_ALARM);
    STATIC_ASSERT(AAUDIO_USAGE_NOTIFICATION == AUDIO_USAGE_NOTIFICATION);
    STATIC_ASSERT(AAUDIO_USAGE_NOTIFICATION_RINGTONE
                  == AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE);
    STATIC_ASSERT(AAUDIO_USAGE_NOTIFICATION_EVENT == AUDIO_USAGE_NOTIFICATION_EVENT);
    STATIC_ASSERT(AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY == AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY);
    STATIC_ASSERT(AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
                  == AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
    STATIC_ASSERT(AAUDIO_USAGE_ASSISTANCE_SONIFICATION == AUDIO_USAGE_ASSISTANCE_SONIFICATION);
    STATIC_ASSERT(AAUDIO_USAGE_GAME == AUDIO_USAGE_GAME);
    STATIC_ASSERT(AAUDIO_USAGE_ASSISTANT == AUDIO_USAGE_ASSISTANT);
    STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_EMERGENCY == AUDIO_USAGE_EMERGENCY);
    STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_SAFETY == AUDIO_USAGE_SAFETY);
    STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS == AUDIO_USAGE_VEHICLE_STATUS);
    STATIC_ASSERT(AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT == AUDIO_USAGE_ANNOUNCEMENT);
    if (usage == AAUDIO_UNSPECIFIED) {
        usage = AAUDIO_USAGE_MEDIA;
    }
    return (audio_usage_t) usage; // same value
}

audio_content_type_t AAudioConvert_contentTypeToInternal(aaudio_content_type_t contentType) {
    // The public aaudio_content_type_t constants are supposed to have the same
    // values as the internal audio_content_type_t values.
    STATIC_ASSERT(AAUDIO_CONTENT_TYPE_MUSIC == AUDIO_CONTENT_TYPE_MUSIC);
    STATIC_ASSERT(AAUDIO_CONTENT_TYPE_SPEECH == AUDIO_CONTENT_TYPE_SPEECH);
    STATIC_ASSERT(AAUDIO_CONTENT_TYPE_SONIFICATION == AUDIO_CONTENT_TYPE_SONIFICATION);
    STATIC_ASSERT(AAUDIO_CONTENT_TYPE_MOVIE == AUDIO_CONTENT_TYPE_MOVIE);
    if (contentType == AAUDIO_UNSPECIFIED) {
        contentType = AAUDIO_CONTENT_TYPE_MUSIC;
    }
    return (audio_content_type_t) contentType; // same value
}

audio_source_t AAudioConvert_inputPresetToAudioSource(aaudio_input_preset_t preset) {
    // The public aaudio_input_preset_t constants are supposed to have the same
    // values as the internal audio_source_t values.
    STATIC_ASSERT(AAUDIO_UNSPECIFIED == AUDIO_SOURCE_DEFAULT);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_GENERIC == AUDIO_SOURCE_MIC);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_CAMCORDER == AUDIO_SOURCE_CAMCORDER);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_RECOGNITION == AUDIO_SOURCE_VOICE_RECOGNITION);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION == AUDIO_SOURCE_VOICE_COMMUNICATION);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_UNPROCESSED == AUDIO_SOURCE_UNPROCESSED);
    STATIC_ASSERT(AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE == AUDIO_SOURCE_VOICE_PERFORMANCE);
    if (preset == AAUDIO_UNSPECIFIED) {
        preset = AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
    }
    return (audio_source_t) preset; // same value
}

audio_flags_mask_t AAudio_computeAudioFlagsMask(
        aaudio_allowed_capture_policy_t policy,
        aaudio_spatialization_behavior_t spatializationBehavior,
        bool isContentSpatialized,
        audio_output_flags_t outputFlags) {
    audio_flags_mask_t flagsMask = AUDIO_FLAG_NONE;
    switch (policy) {
        case AAUDIO_UNSPECIFIED:
        case AAUDIO_ALLOW_CAPTURE_BY_ALL:
            // flagsMask is not modified
            break;
        case AAUDIO_ALLOW_CAPTURE_BY_SYSTEM:
            flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_NO_MEDIA_PROJECTION);
            break;
        case AAUDIO_ALLOW_CAPTURE_BY_NONE:
            flagsMask = static_cast<audio_flags_mask_t>(flagsMask |
                    AUDIO_FLAG_NO_MEDIA_PROJECTION | AUDIO_FLAG_NO_SYSTEM_CAPTURE);
            break;
        default:
            ALOGE("%s() 0x%08X unrecognized capture policy", __func__, policy);
            // flagsMask is not modified
    }

    switch (spatializationBehavior) {
        case AAUDIO_UNSPECIFIED:
        case AAUDIO_SPATIALIZATION_BEHAVIOR_AUTO:
            // flagsMask is not modified
            break;
        case AAUDIO_SPATIALIZATION_BEHAVIOR_NEVER:
            flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_NEVER_SPATIALIZE);
            break;
        default:
            ALOGE("%s() 0x%08X unrecognized spatialization behavior",
                  __func__, spatializationBehavior);
            // flagsMask is not modified
    }

    if (isContentSpatialized) {
        flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_CONTENT_SPATIALIZED);
    }

    if ((outputFlags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) {
        flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_HW_AV_SYNC);
    }
    if ((outputFlags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
        flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_LOW_LATENCY);
    } else if ((outputFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
        flagsMask = static_cast<audio_flags_mask_t>(flagsMask | AUDIO_FLAG_DEEP_BUFFER);
    }

    return flagsMask;
}

audio_flags_mask_t AAudioConvert_privacySensitiveToAudioFlagsMask(
        bool privacySensitive) {
    return privacySensitive ? AUDIO_FLAG_CAPTURE_PRIVATE : AUDIO_FLAG_NONE;
}

audio_channel_mask_t AAudioConvert_aaudioToAndroidChannelLayoutMask(
        aaudio_channel_mask_t channelMask, bool isInput) {
    if (isInput) {
        switch (channelMask) {
            case AAUDIO_CHANNEL_MONO:
                return AUDIO_CHANNEL_IN_MONO;
            case AAUDIO_CHANNEL_STEREO:
                return AUDIO_CHANNEL_IN_STEREO;
            case AAUDIO_CHANNEL_FRONT_BACK:
                return AUDIO_CHANNEL_IN_FRONT_BACK;
            case AAUDIO_CHANNEL_2POINT0POINT2:
                return AUDIO_CHANNEL_IN_2POINT0POINT2;
            case AAUDIO_CHANNEL_2POINT1POINT2:
                return AUDIO_CHANNEL_IN_2POINT1POINT2;
            case AAUDIO_CHANNEL_3POINT0POINT2:
                return AUDIO_CHANNEL_IN_3POINT0POINT2;
            case AAUDIO_CHANNEL_3POINT1POINT2:
                return AUDIO_CHANNEL_IN_3POINT1POINT2;
            case AAUDIO_CHANNEL_5POINT1:
                return AUDIO_CHANNEL_IN_5POINT1;
            default:
                ALOGE("%s() %#x unrecognized", __func__, channelMask);
                return AUDIO_CHANNEL_INVALID;
        }
    } else {
        switch (channelMask) {
            case AAUDIO_CHANNEL_MONO:
                return AUDIO_CHANNEL_OUT_MONO;
            case AAUDIO_CHANNEL_STEREO:
                return AUDIO_CHANNEL_OUT_STEREO;
            case AAUDIO_CHANNEL_2POINT1:
                return AUDIO_CHANNEL_OUT_2POINT1;
            case AAUDIO_CHANNEL_TRI:
                return AUDIO_CHANNEL_OUT_TRI;
            case AAUDIO_CHANNEL_TRI_BACK:
                return AUDIO_CHANNEL_OUT_TRI_BACK;
            case AAUDIO_CHANNEL_3POINT1:
                return AUDIO_CHANNEL_OUT_3POINT1;
            case AAUDIO_CHANNEL_2POINT0POINT2:
                return AUDIO_CHANNEL_OUT_2POINT0POINT2;
            case AAUDIO_CHANNEL_2POINT1POINT2:
                return AUDIO_CHANNEL_OUT_2POINT1POINT2;
            case AAUDIO_CHANNEL_3POINT0POINT2:
                return AUDIO_CHANNEL_OUT_3POINT0POINT2;
            case AAUDIO_CHANNEL_3POINT1POINT2:
                return AUDIO_CHANNEL_OUT_3POINT1POINT2;
            case AAUDIO_CHANNEL_QUAD:
                return AUDIO_CHANNEL_OUT_QUAD;
            case AAUDIO_CHANNEL_QUAD_SIDE:
                return AUDIO_CHANNEL_OUT_QUAD_SIDE;
            case AAUDIO_CHANNEL_SURROUND:
                return AUDIO_CHANNEL_OUT_SURROUND;
            case AAUDIO_CHANNEL_PENTA:
                return AUDIO_CHANNEL_OUT_PENTA;
            case AAUDIO_CHANNEL_5POINT1:
                return AUDIO_CHANNEL_OUT_5POINT1;
            case AAUDIO_CHANNEL_5POINT1_SIDE:
                return AUDIO_CHANNEL_OUT_5POINT1_SIDE;
            case AAUDIO_CHANNEL_5POINT1POINT2:
                return AUDIO_CHANNEL_OUT_5POINT1POINT2;
            case AAUDIO_CHANNEL_5POINT1POINT4:
                return AUDIO_CHANNEL_OUT_5POINT1POINT4;
            case AAUDIO_CHANNEL_6POINT1:
                return AUDIO_CHANNEL_OUT_6POINT1;
            case AAUDIO_CHANNEL_7POINT1:
                return AUDIO_CHANNEL_OUT_7POINT1;
            case AAUDIO_CHANNEL_7POINT1POINT2:
                return AUDIO_CHANNEL_OUT_7POINT1POINT2;
            case AAUDIO_CHANNEL_7POINT1POINT4:
                return AUDIO_CHANNEL_OUT_7POINT1POINT4;
            case AAUDIO_CHANNEL_9POINT1POINT4:
                return AUDIO_CHANNEL_OUT_9POINT1POINT4;
            case AAUDIO_CHANNEL_9POINT1POINT6:
                return AUDIO_CHANNEL_OUT_9POINT1POINT6;
            default:
                ALOGE("%s() %#x unrecognized", __func__, channelMask);
                return AUDIO_CHANNEL_INVALID;
        }
    }
}

aaudio_channel_mask_t AAudioConvert_androidToAAudioChannelLayoutMask(
        audio_channel_mask_t channelMask, bool isInput) {
    if (isInput) {
        switch (channelMask) {
            case AUDIO_CHANNEL_IN_MONO:
                return AAUDIO_CHANNEL_MONO;
            case AUDIO_CHANNEL_IN_STEREO:
                return AAUDIO_CHANNEL_STEREO;
            case AUDIO_CHANNEL_IN_FRONT_BACK:
                return AAUDIO_CHANNEL_FRONT_BACK;
            case AUDIO_CHANNEL_IN_2POINT0POINT2:
                return AAUDIO_CHANNEL_2POINT0POINT2;
            case AUDIO_CHANNEL_IN_2POINT1POINT2:
                return AAUDIO_CHANNEL_2POINT1POINT2;
            case AUDIO_CHANNEL_IN_3POINT0POINT2:
                return AAUDIO_CHANNEL_3POINT0POINT2;
            case AUDIO_CHANNEL_IN_3POINT1POINT2:
                return AAUDIO_CHANNEL_3POINT1POINT2;
            case AUDIO_CHANNEL_IN_5POINT1:
                return AAUDIO_CHANNEL_5POINT1;
            default:
                ALOGE("%s() %#x unrecognized", __func__, channelMask);
                return AAUDIO_CHANNEL_INVALID;
        }
    } else {
        switch (channelMask) {
            case AUDIO_CHANNEL_OUT_MONO:
                return AAUDIO_CHANNEL_MONO;
            case AUDIO_CHANNEL_OUT_STEREO:
                return AAUDIO_CHANNEL_STEREO;
            case AUDIO_CHANNEL_OUT_2POINT1:
                return AAUDIO_CHANNEL_2POINT1;
            case AUDIO_CHANNEL_OUT_TRI:
                return AAUDIO_CHANNEL_TRI;
            case AUDIO_CHANNEL_OUT_TRI_BACK:
                return AAUDIO_CHANNEL_TRI_BACK;
            case AUDIO_CHANNEL_OUT_3POINT1:
                return AAUDIO_CHANNEL_3POINT1;
            case AUDIO_CHANNEL_OUT_2POINT0POINT2:
                return AAUDIO_CHANNEL_2POINT0POINT2;
            case AUDIO_CHANNEL_OUT_2POINT1POINT2:
                return AAUDIO_CHANNEL_2POINT1POINT2;
            case AUDIO_CHANNEL_OUT_3POINT0POINT2:
                return AAUDIO_CHANNEL_3POINT0POINT2;
            case AUDIO_CHANNEL_OUT_3POINT1POINT2:
                return AAUDIO_CHANNEL_3POINT1POINT2;
            case AUDIO_CHANNEL_OUT_QUAD:
                return AAUDIO_CHANNEL_QUAD;
            case AUDIO_CHANNEL_OUT_QUAD_SIDE:
                return AAUDIO_CHANNEL_QUAD_SIDE;
            case AUDIO_CHANNEL_OUT_SURROUND:
                return AAUDIO_CHANNEL_SURROUND;
            case AUDIO_CHANNEL_OUT_PENTA:
                return AAUDIO_CHANNEL_PENTA;
            case AUDIO_CHANNEL_OUT_5POINT1:
                return AAUDIO_CHANNEL_5POINT1;
            case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
                return AAUDIO_CHANNEL_5POINT1_SIDE;
            case AUDIO_CHANNEL_OUT_5POINT1POINT2:
                return AAUDIO_CHANNEL_5POINT1POINT2;
            case AUDIO_CHANNEL_OUT_5POINT1POINT4:
                return AAUDIO_CHANNEL_5POINT1POINT4;
            case AUDIO_CHANNEL_OUT_6POINT1:
                return AAUDIO_CHANNEL_6POINT1;
            case AUDIO_CHANNEL_OUT_7POINT1:
                return AAUDIO_CHANNEL_7POINT1;
            case AUDIO_CHANNEL_OUT_7POINT1POINT2:
                return AAUDIO_CHANNEL_7POINT1POINT2;
            case AUDIO_CHANNEL_OUT_7POINT1POINT4:
                return AAUDIO_CHANNEL_7POINT1POINT4;
            case AUDIO_CHANNEL_OUT_9POINT1POINT4:
                return AAUDIO_CHANNEL_9POINT1POINT4;
            case AUDIO_CHANNEL_OUT_9POINT1POINT6:
                return AAUDIO_CHANNEL_9POINT1POINT6;
            default:
                ALOGE("%s() %#x unrecognized", __func__, channelMask);
                return AAUDIO_CHANNEL_INVALID;
        }
    }
}

int32_t AAudioConvert_channelMaskToCount(aaudio_channel_mask_t channelMask) {
    return __builtin_popcount(channelMask & ~AAUDIO_CHANNEL_BIT_INDEX);
}

aaudio_channel_mask_t AAudioConvert_channelCountToMask(int32_t channelCount) {
    if (channelCount < 0 || channelCount > AUDIO_CHANNEL_COUNT_MAX) {
        return AAUDIO_CHANNEL_INVALID;
    }

    if (channelCount == 0) {
        return AAUDIO_UNSPECIFIED;
    }

    // Return index mask if the channel count is greater than 2.
    return AAUDIO_CHANNEL_BIT_INDEX | ((1 << channelCount) - 1);
}

aaudio_channel_mask_t AAudioConvert_androidToAAudioChannelIndexMask(
        audio_channel_mask_t channelMask) {
    if (audio_channel_mask_get_representation(channelMask) != AUDIO_CHANNEL_REPRESENTATION_INDEX) {
        ALOGE("%s() %#x not an index mask", __func__, channelMask);
        return AAUDIO_CHANNEL_INVALID;
    }
    return (channelMask & ~AUDIO_CHANNEL_INDEX_HDR) | AAUDIO_CHANNEL_BIT_INDEX;
}

audio_channel_mask_t AAudioConvert_aaudioToAndroidChannelIndexMask(
        aaudio_channel_mask_t channelMask) {
    if (!AAudio_isChannelIndexMask(channelMask)) {
        ALOGE("%s() %#x not an index mask", __func__, channelMask);
        return AUDIO_CHANNEL_INVALID;
    }
    return audio_channel_mask_for_index_assignment_from_count(
            AAudioConvert_channelMaskToCount(channelMask));
}

aaudio_channel_mask_t AAudioConvert_androidToAAudioChannelMask(
        audio_channel_mask_t channelMask, bool isInput, bool indexMaskRequired) {
    if (audio_channel_mask_get_representation(channelMask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
        return AAudioConvert_androidToAAudioChannelIndexMask(channelMask);
    }
    if (indexMaskRequired) {
        // Require index mask, `channelMask` here is a position mask.
        const int channelCount = isInput ? audio_channel_count_from_in_mask(channelMask)
                                         : audio_channel_count_from_out_mask(channelMask);
        return AAudioConvert_channelCountToMask(channelCount);
    }
    return AAudioConvert_androidToAAudioChannelLayoutMask(channelMask, isInput);
}

audio_channel_mask_t AAudioConvert_aaudioToAndroidChannelMask(
        aaudio_channel_mask_t channelMask, bool isInput) {
    return AAudio_isChannelIndexMask(channelMask)
            ? AAudioConvert_aaudioToAndroidChannelIndexMask(channelMask)
            : AAudioConvert_aaudioToAndroidChannelLayoutMask(channelMask, isInput);
}

bool AAudio_isChannelIndexMask(aaudio_channel_mask_t channelMask) {
    return (channelMask & AAUDIO_CHANNEL_BIT_INDEX) == AAUDIO_CHANNEL_BIT_INDEX;
}

audio_channel_mask_t AAudio_getChannelMaskForOpen(
        aaudio_channel_mask_t channelMask, int32_t samplesPerFrame, bool isInput) {
    if (channelMask != AAUDIO_UNSPECIFIED) {
        if (AAudio_isChannelIndexMask(channelMask) && samplesPerFrame <= 2) {
            // When it is index mask and the count is less than 3, use position mask
            // instead of index mask for opening a stream. This may need to be revisited
            // when making channel index mask public.
            return isInput ? audio_channel_in_mask_from_count(samplesPerFrame)
                           : audio_channel_out_mask_from_count(samplesPerFrame);
        }
        return AAudioConvert_aaudioToAndroidChannelMask(channelMask, isInput);
    }

    // Return stereo when unspecified.
    return isInput ? AUDIO_CHANNEL_IN_STEREO : AUDIO_CHANNEL_OUT_STEREO;
}

int32_t AAudioConvert_framesToBytes(int32_t numFrames,
                                    int32_t bytesPerFrame,
                                    int32_t *sizeInBytes) {
    *sizeInBytes = 0;

    if (numFrames < 0 || bytesPerFrame < 0) {
        ALOGE("negative size, numFrames = %d, frameSize = %d", numFrames, bytesPerFrame);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }

    // Prevent numeric overflow.
    if (numFrames > (INT32_MAX / bytesPerFrame)) {
        ALOGE("size overflow, numFrames = %d, frameSize = %d", numFrames, bytesPerFrame);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }

    *sizeInBytes = numFrames * bytesPerFrame;
    return AAUDIO_OK;
}

int32_t AAudioProperty_getWakeupDelayMicros() {
    const int32_t minMicros = 0; // arbitrary
    const int32_t defaultMicros = 200; // arbitrary, based on some observed jitter
    const int32_t maxMicros = 5000; // arbitrary, probably don't want more than 500
    int32_t prop = property_get_int32(AAUDIO_PROP_WAKEUP_DELAY_USEC, defaultMicros);
    if (prop < minMicros) {
        ALOGW("AAudioProperty_getWakeupDelayMicros: clipped %d to %d", prop, minMicros);
        prop = minMicros;
    } else if (prop > maxMicros) {
        ALOGW("AAudioProperty_getWakeupDelayMicros: clipped %d to %d", prop, maxMicros);
        prop = maxMicros;
    }
    return prop;
}

int32_t AAudioProperty_getMinimumSleepMicros() {
    const int32_t minMicros = 1; // arbitrary
    // Higher values can increase latency for moderate workloads.
    // Short values can cause the CPU to short cycle if there is a bug in
    // calculating the wakeup times.
    const int32_t defaultMicros = 100; // arbitrary
    const int32_t maxMicros = 200; // arbitrary
    int32_t prop = property_get_int32(AAUDIO_PROP_MINIMUM_SLEEP_USEC, defaultMicros);
    if (prop < minMicros) {
        ALOGW("AAudioProperty_getMinimumSleepMicros: clipped %d to %d", prop, minMicros);
        prop = minMicros;
    } else if (prop > maxMicros) {
        ALOGW("AAudioProperty_getMinimumSleepMicros: clipped %d to %d", prop, maxMicros);
        prop = maxMicros;
    }
    return prop;
}

static int32_t AAudioProperty_getMMapOffsetMicros(const char *functionName,
        const char *propertyName) {
    const int32_t minMicros = -20000; // arbitrary
    const int32_t defaultMicros = 0;  // arbitrary
    const int32_t maxMicros =  20000; // arbitrary
    int32_t prop = property_get_int32(propertyName, defaultMicros);
    if (prop < minMicros) {
        ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
        prop = minMicros;
    } else if (prop > maxMicros) {
        ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
        prop = maxMicros;
    }
    return prop;
}

int32_t AAudioProperty_getInputMMapOffsetMicros() {
    return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_INPUT_MMAP_OFFSET_USEC);
}

int32_t AAudioProperty_getOutputMMapOffsetMicros() {
    return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_OUTPUT_MMAP_OFFSET_USEC);
}

int32_t AAudioProperty_getLogMask() {
    return property_get_int32(AAUDIO_PROP_LOG_MASK, 0);
}

aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
    aaudio_result_t result = AAUDIO_OK;
    switch (state) {
// Proceed with flushing.
        case AAUDIO_STREAM_STATE_OPEN:
        case AAUDIO_STREAM_STATE_PAUSED:
        case AAUDIO_STREAM_STATE_STOPPED:
        case AAUDIO_STREAM_STATE_FLUSHED:
            break;

// Transition from one inactive state to another.
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTED:
        case AAUDIO_STREAM_STATE_STOPPING:
        case AAUDIO_STREAM_STATE_PAUSING:
        case AAUDIO_STREAM_STATE_FLUSHING:
        case AAUDIO_STREAM_STATE_CLOSING:
        case AAUDIO_STREAM_STATE_CLOSED:
        case AAUDIO_STREAM_STATE_DISCONNECTED:
        default:
            ALOGE("can only flush stream when PAUSED, OPEN or STOPPED, state = %s",
                  aaudio::AudioGlobal_convertStreamStateToText(state));
            result =  AAUDIO_ERROR_INVALID_STATE;
            break;
    }
    return result;
}

namespace {

aaudio_policy_t aidl2legacy_aaudio_policy(AudioMMapPolicy aidl) {
    switch (aidl) {
        case AudioMMapPolicy::NEVER:
            return AAUDIO_POLICY_NEVER;
        case AudioMMapPolicy::AUTO:
            return AAUDIO_POLICY_AUTO;
        case AudioMMapPolicy::ALWAYS:
            return AAUDIO_POLICY_ALWAYS;
        case AudioMMapPolicy::UNSPECIFIED:
        default:
            return AAUDIO_UNSPECIFIED;
    }
}

} // namespace

aaudio_policy_t AAudio_getAAudioPolicy(const std::vector<AudioMMapPolicyInfo>& policyInfos,
                                       AudioMMapPolicy defaultPolicy) {
    AudioMMapPolicy policy = defaultPolicy;
    for (const auto& policyInfo : policyInfos) {
        if (policyInfo.mmapPolicy == AudioMMapPolicy::NEVER) {
            policy = policyInfo.mmapPolicy;
        } else if (policyInfo.mmapPolicy == AudioMMapPolicy::AUTO ||
                   policyInfo.mmapPolicy == AudioMMapPolicy::ALWAYS) {
            return AAUDIO_POLICY_AUTO;
        }
    }
    return aidl2legacy_aaudio_policy(policy);
}

audio_devices_t AAudioConvert_aaudioToAndroidDeviceType(AAudio_DeviceType device,
                                                        aaudio_direction_t direction) {
    if (direction == AAUDIO_DIRECTION_INPUT) {
        switch (device) {
            case AAUDIO_DEVICE_BUILTIN_MIC:
                return AUDIO_DEVICE_IN_BUILTIN_MIC;
            case AAUDIO_DEVICE_BLUETOOTH_SCO:
                return AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
            case AAUDIO_DEVICE_WIRED_HEADSET:
                return AUDIO_DEVICE_IN_WIRED_HEADSET;
            case AAUDIO_DEVICE_HDMI:
                return AUDIO_DEVICE_IN_HDMI;
            case AAUDIO_DEVICE_TELEPHONY:
                return AUDIO_DEVICE_IN_TELEPHONY_RX;
            case AAUDIO_DEVICE_DOCK:
                return AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
            case AAUDIO_DEVICE_DOCK_ANALOG:
                return AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET;
            case AAUDIO_DEVICE_USB_ACCESSORY:
                return AUDIO_DEVICE_IN_USB_ACCESSORY;
            case AAUDIO_DEVICE_USB_DEVICE:
                return AUDIO_DEVICE_IN_USB_DEVICE;
            case AAUDIO_DEVICE_USB_HEADSET:
                return AUDIO_DEVICE_IN_USB_HEADSET;
            case AAUDIO_DEVICE_FM_TUNER:
                return AUDIO_DEVICE_IN_FM_TUNER;
            case AAUDIO_DEVICE_TV_TUNER:
                return AUDIO_DEVICE_IN_TV_TUNER;
            case AAUDIO_DEVICE_LINE_ANALOG:
                return AUDIO_DEVICE_IN_LINE;
            case AAUDIO_DEVICE_LINE_DIGITAL:
                return AUDIO_DEVICE_IN_SPDIF;
            case AAUDIO_DEVICE_BLUETOOTH_A2DP:
                return AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
            case AAUDIO_DEVICE_IP:
                return AUDIO_DEVICE_IN_IP;
            case AAUDIO_DEVICE_BUS:
                return AUDIO_DEVICE_IN_BUS;
            case AAUDIO_DEVICE_REMOTE_SUBMIX:
                return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
            case AAUDIO_DEVICE_BLE_HEADSET:
                return AUDIO_DEVICE_IN_BLE_HEADSET;
            case AAUDIO_DEVICE_HDMI_ARC:
                return AUDIO_DEVICE_IN_HDMI_ARC;
            case AAUDIO_DEVICE_HDMI_EARC:
                return AUDIO_DEVICE_IN_HDMI_EARC;
            default:
                break;
        }
    } else {
        switch (device) {
            case AAUDIO_DEVICE_BUILTIN_EARPIECE:
                return AUDIO_DEVICE_OUT_EARPIECE;
            case AAUDIO_DEVICE_BUILTIN_SPEAKER:
                return AUDIO_DEVICE_OUT_SPEAKER;
            case AAUDIO_DEVICE_WIRED_HEADSET:
                return AUDIO_DEVICE_OUT_WIRED_HEADSET;
            case AAUDIO_DEVICE_WIRED_HEADPHONES:
                return AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
            case AAUDIO_DEVICE_LINE_ANALOG:
                return AUDIO_DEVICE_OUT_LINE;
            case AAUDIO_DEVICE_LINE_DIGITAL:
                return AUDIO_DEVICE_OUT_SPDIF;
            case AAUDIO_DEVICE_BLUETOOTH_SCO:
                return AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
            case AAUDIO_DEVICE_BLUETOOTH_A2DP:
                return AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
            case AAUDIO_DEVICE_HDMI:
                return AUDIO_DEVICE_OUT_HDMI;
            case AAUDIO_DEVICE_HDMI_ARC:
                return AUDIO_DEVICE_OUT_HDMI_ARC;
            case AAUDIO_DEVICE_HDMI_EARC:
                return AUDIO_DEVICE_OUT_HDMI_EARC;
            case AAUDIO_DEVICE_USB_DEVICE:
                return AUDIO_DEVICE_OUT_USB_DEVICE;
            case AAUDIO_DEVICE_USB_HEADSET:
                return AUDIO_DEVICE_OUT_USB_HEADSET;
            case AAUDIO_DEVICE_USB_ACCESSORY:
                return AUDIO_DEVICE_OUT_USB_ACCESSORY;
            case AAUDIO_DEVICE_DOCK:
                return AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
            case AAUDIO_DEVICE_DOCK_ANALOG:
                return AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
            case AAUDIO_DEVICE_FM:
                return AUDIO_DEVICE_OUT_FM;
            case AAUDIO_DEVICE_TELEPHONY:
                return AUDIO_DEVICE_OUT_TELEPHONY_TX;
            case AAUDIO_DEVICE_AUX_LINE:
                return AUDIO_DEVICE_OUT_AUX_LINE;
            case AAUDIO_DEVICE_IP:
                return AUDIO_DEVICE_OUT_IP;
            case AAUDIO_DEVICE_BUS:
                return AUDIO_DEVICE_OUT_BUS;
            case AAUDIO_DEVICE_HEARING_AID:
                return AUDIO_DEVICE_OUT_HEARING_AID;
            case AAUDIO_DEVICE_BUILTIN_SPEAKER_SAFE:
                return AUDIO_DEVICE_OUT_SPEAKER_SAFE;
            case AAUDIO_DEVICE_REMOTE_SUBMIX:
                return AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
            case AAUDIO_DEVICE_BLE_HEADSET:
                return AUDIO_DEVICE_OUT_BLE_HEADSET;
            case AAUDIO_DEVICE_BLE_SPEAKER:
                return AUDIO_DEVICE_OUT_BLE_SPEAKER;
            case AAUDIO_DEVICE_BLE_BROADCAST:
                return AUDIO_DEVICE_OUT_BLE_BROADCAST;
            default:
                break;
        }
    }
    return AUDIO_DEVICE_NONE;
}

aaudio_policy_t AAudioConvert_androidToAAudioMMapPolicy(AudioMMapPolicy policy) {
    switch (policy) {
        case AudioMMapPolicy::AUTO:
            return AAUDIO_POLICY_AUTO;
        case AudioMMapPolicy::ALWAYS:
            return AAUDIO_POLICY_ALWAYS;
        case AudioMMapPolicy::NEVER:
        case AudioMMapPolicy::UNSPECIFIED:
        default:
            return AAUDIO_POLICY_NEVER;
    }
}
