diff options
Diffstat (limited to 'services')
166 files changed, 16678 insertions, 8031 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 44d2553..fee2347 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -39,6 +39,9 @@ LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ Threads.cpp \ Tracks.cpp \ + AudioHwDevice.cpp \ + AudioStreamOut.cpp \ + SpdifStreamOut.cpp \ Effects.cpp \ AudioMixer.cpp.arm \ PatchPanel.cpp @@ -52,6 +55,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libaudioresampler \ + libaudiospdif \ libaudioutils \ libcommon_time_client \ libcutils \ @@ -74,9 +78,17 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_MODULE:= libaudioflinger LOCAL_32_BIT_ONLY := true -LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp -LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp -LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp +LOCAL_SRC_FILES += \ + AudioWatchdog.cpp \ + FastCapture.cpp \ + FastCaptureDumpState.cpp \ + FastCaptureState.cpp \ + FastMixer.cpp \ + FastMixerDumpState.cpp \ + FastMixerState.cpp \ + FastThread.cpp \ + FastThreadDumpState.cpp \ + FastThreadState.cpp LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 993db73..f3206cb 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -185,7 +185,8 @@ AudioFlinger::AudioFlinger() char value[PROPERTY_VALUE_MAX]; bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); if (doLog) { - mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY); + mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", + MemoryHeapBase::READ_ONLY); } #ifdef TEE_SINK @@ -271,7 +272,7 @@ static const char * const audio_interfaces[] = { }; #define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0]))) -AudioFlinger::AudioHwDevice* AudioFlinger::findSuitableHwDev_l( +AudioHwDevice* AudioFlinger::findSuitableHwDev_l( audio_module_handle_t module, audio_devices_t devices) { @@ -401,6 +402,9 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) String8 result(kClientLockedString); write(fd, result.string(), result.size()); } + + EffectDumpEffects(fd); + dumpClients(fd, args); if (clientLocked) { mClientLock.unlock(); @@ -822,14 +826,20 @@ bool AudioFlinger::getMicMute() const if (ret != NO_ERROR) { return false; } - + bool mute = true; bool state = AUDIO_MODE_INVALID; AutoMutex lock(mHardwareLock); - audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; - dev->get_mic_mute(dev, &state); + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); + status_t result = dev->get_mic_mute(dev, &state); + if (result == NO_ERROR) { + mute = mute && state; + } + } mHardwareStatus = AUDIO_HW_IDLE; - return state; + + return mute; } status_t AudioFlinger::setMasterMute(bool muted) @@ -1706,8 +1716,6 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_ mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; - audio_stream_out_t *outStream = NULL; - // FOR TESTING ONLY: // This if statement allows overriding the audio policy settings // and forcing a specific format or channel mask to the HAL/Sink device for testing. @@ -1729,25 +1737,18 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_ } } - status_t status = hwDevHal->open_output_stream(hwDevHal, - *output, - devices, - flags, - config, - &outStream, - address.string()); + AudioStreamOut *outputStream = NULL; + status_t status = outHwDev->openOutputStream( + &outputStream, + *output, + devices, + flags, + config, + address.string()); mHardwareStatus = AUDIO_HW_IDLE; - ALOGV("openOutput_l() openOutputStream returned output %p, sampleRate %d, Format %#x, " - "channelMask %#x, status %d", - outStream, - config->sample_rate, - config->format, - config->channel_mask, - status); - if (status == NO_ERROR && outStream != NULL) { - AudioStreamOut *outputStream = new AudioStreamOut(outHwDev, outStream, flags); + if (status == NO_ERROR) { PlaybackThread *thread; if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { @@ -1777,7 +1778,7 @@ status_t AudioFlinger::openOutput(audio_module_handle_t module, uint32_t *latencyMs, audio_output_flags_t flags) { - ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", + ALOGI("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", module, (devices != NULL) ? *devices : 0, config->sample_rate, @@ -1947,18 +1948,18 @@ status_t AudioFlinger::restoreOutput(audio_io_handle_t output) status_t AudioFlinger::openInput(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, - audio_devices_t *device, + audio_devices_t *devices, const String8& address, audio_source_t source, audio_input_flags_t flags) { Mutex::Autolock _l(mLock); - if (*device == AUDIO_DEVICE_NONE) { + if (*devices == AUDIO_DEVICE_NONE) { return BAD_VALUE; } - sp<RecordThread> thread = openInput_l(module, input, config, *device, address, source, flags); + sp<RecordThread> thread = openInput_l(module, input, config, *devices, address, source, flags); if (thread != 0) { // notify client processes of the new input creation @@ -1971,12 +1972,12 @@ status_t AudioFlinger::openInput(audio_module_handle_t module, sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module, audio_io_handle_t *input, audio_config_t *config, - audio_devices_t device, + audio_devices_t devices, const String8& address, audio_source_t source, audio_input_flags_t flags) { - AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device); + AudioHwDevice *inHwDev = findSuitableHwDev_l(module, devices); if (inHwDev == NULL) { *input = AUDIO_IO_HANDLE_NONE; return 0; @@ -1989,7 +1990,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m audio_config_t halconfig = *config; audio_hw_device_t *inHwHal = inHwDev->hwDevice(); audio_stream_in_t *inStream = NULL; - status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + status_t status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig, &inStream, flags, address.string(), source); ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d" ", Format %#x, Channels %x, flags %#x, status %d addr %s", @@ -2011,7 +2012,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m // FIXME describe the change proposed by HAL (save old values so we can log them here) ALOGV("openInput_l() reopening with proposed sampling rate and channel mask"); inStream = NULL; - status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + status = inHwHal->open_input_stream(inHwHal, *input, devices, &halconfig, &inStream, flags, address.string(), source); // FIXME log this new status; HAL should not propose any further changes } @@ -2076,7 +2077,7 @@ sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t m inputStream, *input, primaryOutputDevice_l(), - device + devices #ifdef TEE_SINK , teeSink #endif @@ -2799,13 +2800,13 @@ bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule> struct Entry { -#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav - char mName[MAX_NAME]; +#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21 + char mFileName[TEE_MAX_FILENAME]; }; int comparEntry(const void *p1, const void *p2) { - return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName); + return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName); } #ifdef TEE_SINK @@ -2824,11 +2825,11 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand DIR *dir = opendir(teePath); teePath[teePathLen++] = '/'; if (dir != NULL) { -#define MAX_SORT 20 // number of entries to sort -#define MAX_KEEP 10 // number of entries to keep - struct Entry entries[MAX_SORT]; +#define TEE_MAX_SORT 20 // number of entries to sort +#define TEE_MAX_KEEP 10 // number of entries to keep + struct Entry entries[TEE_MAX_SORT]; size_t entryCount = 0; - while (entryCount < MAX_SORT) { + while (entryCount < TEE_MAX_SORT) { struct dirent de; struct dirent *result = NULL; int rc = readdir_r(dir, &de, &result); @@ -2845,17 +2846,17 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand } // ignore non .wav file entries size_t nameLen = strlen(de.d_name); - if (nameLen <= 4 || nameLen >= MAX_NAME || + if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME || strcmp(&de.d_name[nameLen - 4], ".wav")) { continue; } - strcpy(entries[entryCount++].mName, de.d_name); + strcpy(entries[entryCount++].mFileName, de.d_name); } (void) closedir(dir); - if (entryCount > MAX_KEEP) { + if (entryCount > TEE_MAX_KEEP) { qsort(entries, entryCount, sizeof(Entry), comparEntry); - for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) { - strcpy(&teePath[teePathLen], entries[i].mName); + for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) { + strcpy(&teePath[teePathLen], entries[i].mFileName); (void) unlink(teePath); } } @@ -2939,4 +2940,4 @@ status_t AudioFlinger::onTransact( return BnAudioFlinger::onTransact(code, data, reply, flags); } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index aa0af1f..c7d9161 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -56,6 +56,9 @@ #include <media/nbaio/NBAIO.h> #include "AudioWatchdog.h" #include "AudioMixer.h" +#include "AudioStreamOut.h" +#include "SpdifStreamOut.h" +#include "AudioHwDevice.h" #include <powermanager/IPowerManager.h> @@ -311,7 +314,6 @@ public: wp<RefBase> cookie); private: - class AudioHwDevice; // fwd declaration for findSuitableHwDev_l audio_mode_t getMode() const { return mMode; } @@ -449,7 +451,7 @@ private: class EffectModule; class EffectHandle; class EffectChain; - struct AudioStreamOut; + struct AudioStreamIn; struct stream_type_t { @@ -586,57 +588,11 @@ private: // Return true if the effect was found in mOrphanEffectChains, false otherwise. bool updateOrphanEffectChains(const sp<EffectModule>& effect); - class AudioHwDevice { - public: - enum Flags { - AHWD_CAN_SET_MASTER_VOLUME = 0x1, - AHWD_CAN_SET_MASTER_MUTE = 0x2, - }; - - AudioHwDevice(audio_module_handle_t handle, - const char *moduleName, - audio_hw_device_t *hwDevice, - Flags flags) - : mHandle(handle), mModuleName(strdup(moduleName)) - , mHwDevice(hwDevice) - , mFlags(flags) { } - /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); } - - bool canSetMasterVolume() const { - return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME)); - } - - bool canSetMasterMute() const { - return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); - } - - audio_module_handle_t handle() const { return mHandle; } - const char *moduleName() const { return mModuleName; } - audio_hw_device_t *hwDevice() const { return mHwDevice; } - uint32_t version() const { return mHwDevice->common.version; } - private: - const audio_module_handle_t mHandle; - const char * const mModuleName; - audio_hw_device_t * const mHwDevice; - const Flags mFlags; - }; - - // AudioStreamOut and AudioStreamIn are immutable, so their fields are const. + // AudioStreamIn is immutable, so their fields are const. // For emphasis, we could also make all pointers to them be "const *", // but that would clutter the code unnecessarily. - struct AudioStreamOut { - AudioHwDevice* const audioHwDev; - audio_stream_out_t* const stream; - const audio_output_flags_t flags; - - audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); } - - AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out, audio_output_flags_t flags) : - audioHwDev(dev), stream(out), flags(flags) {} - }; - struct AudioStreamIn { AudioHwDevice* const audioHwDev; audio_stream_in_t* const stream; @@ -796,9 +752,13 @@ private: #undef INCLUDING_FROM_AUDIOFLINGER_H const char *formatToString(audio_format_t format); +String8 inputFlagsToString(audio_input_flags_t flags); +String8 outputFlagsToString(audio_output_flags_t flags); +String8 devicesToString(audio_devices_t devices); +const char *sourceToString(audio_source_t source); // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_AUDIO_FLINGER_H diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp new file mode 100644 index 0000000..09d86ea --- /dev/null +++ b/services/audioflinger/AudioHwDevice.cpp @@ -0,0 +1,94 @@ +/* +** +** Copyright 2007, 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 "AudioHwDevice" +//#define LOG_NDEBUG 0 + +#include <hardware/audio.h> +#include <utils/Log.h> + +#include <audio_utils/spdif/SPDIFEncoder.h> + +#include "AudioHwDevice.h" +#include "AudioStreamOut.h" +#include "SpdifStreamOut.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +status_t AudioHwDevice::openOutputStream( + AudioStreamOut **ppStreamOut, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address) +{ + + struct audio_config originalConfig = *config; + AudioStreamOut *outputStream = new AudioStreamOut(this, flags); + + // Try to open the HAL first using the current format. + ALOGV("AudioHwDevice::openOutputStream(), try " + " sampleRate %d, Format %#x, " + "channelMask %#x", + config->sample_rate, + config->format, + config->channel_mask); + status_t status = outputStream->open(handle, devices, config, address); + + if (status != NO_ERROR) { + delete outputStream; + outputStream = NULL; + + // FIXME Look at any modification to the config. + // The HAL might modify the config to suggest a wrapped format. + // Log this so we can see what the HALs are doing. + ALOGI("AudioHwDevice::openOutputStream(), HAL returned" + " sampleRate %d, Format %#x, " + "channelMask %#x, status %d", + config->sample_rate, + config->format, + config->channel_mask, + status); + + // If the data is encoded then try again using wrapped PCM. + bool wrapperNeeded = !audio_is_linear_pcm(originalConfig.format) + && ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) + && ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0); + + // FIXME - Add isEncodingSupported() query to SPDIF wrapper then + // call it from here. + if (wrapperNeeded) { + outputStream = new SpdifStreamOut(this, flags); + status = outputStream->open(handle, devices, &originalConfig, address); + if (status != NO_ERROR) { + ALOGE("ERROR - AudioHwDevice::openOutputStream(), SPDIF open returned %d", + status); + delete outputStream; + outputStream = NULL; + } + } + } + + *ppStreamOut = outputStream; + return status; +} + + +}; // namespace android diff --git a/services/audioflinger/AudioHwDevice.h b/services/audioflinger/AudioHwDevice.h new file mode 100644 index 0000000..b9f65c1 --- /dev/null +++ b/services/audioflinger/AudioHwDevice.h @@ -0,0 +1,88 @@ +/* +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_AUDIO_HW_DEVICE_H +#define ANDROID_AUDIO_HW_DEVICE_H + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <hardware/audio.h> +#include <utils/Errors.h> +#include <system/audio.h> + + +namespace android { + +class AudioStreamOut; + +class AudioHwDevice { +public: + enum Flags { + AHWD_CAN_SET_MASTER_VOLUME = 0x1, + AHWD_CAN_SET_MASTER_MUTE = 0x2, + }; + + AudioHwDevice(audio_module_handle_t handle, + const char *moduleName, + audio_hw_device_t *hwDevice, + Flags flags) + : mHandle(handle) + , mModuleName(strdup(moduleName)) + , mHwDevice(hwDevice) + , mFlags(flags) { } + virtual ~AudioHwDevice() { free((void *)mModuleName); } + + bool canSetMasterVolume() const { + return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME)); + } + + bool canSetMasterMute() const { + return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); + } + + audio_module_handle_t handle() const { return mHandle; } + const char *moduleName() const { return mModuleName; } + audio_hw_device_t *hwDevice() const { return mHwDevice; } + uint32_t version() const { return mHwDevice->common.version; } + + /** This method creates and opens the audio hardware output stream. + * The "address" parameter qualifies the "devices" audio device type if needed. + * The format format depends on the device type: + * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC" + * - USB devices use the ALSA card and device numbers in the form "card=X;device=Y" + * - Other devices may use a number or any other string. + */ + status_t openOutputStream( + AudioStreamOut **ppStreamOut, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + const char *address); + +private: + const audio_module_handle_t mHandle; + const char * const mModuleName; + audio_hw_device_t * const mHwDevice; + const Flags mFlags; +}; + +} // namespace android + +#endif // ANDROID_AUDIO_HW_DEVICE_H diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index fd28ea1..dddca02 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -69,9 +69,9 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #endif -// Set kUseNewMixer to true to use the new mixer engine. Otherwise the -// original code will be used. This is false for now. -static const bool kUseNewMixer = false; +// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the +// original code will be used for stereo sinks, the new mixer for multichannel. +static const bool kUseNewMixer = true; // Set kUseFloat to true to allow floating input into the mixer engine. // If kUseNewMixer is false, this is ignored or may be overridden internally @@ -341,11 +341,46 @@ AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputC ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu", this, format, inputChannelMask, outputChannelMask, mInputChannels, mOutputChannels); - // TODO: consider channel representation in index array formulation - // We ignore channel representation, and just use the bits. - memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry), - audio_channel_mask_get_bits(outputChannelMask), - audio_channel_mask_get_bits(inputChannelMask)); + + const audio_channel_representation_t inputRepresentation = + audio_channel_mask_get_representation(inputChannelMask); + const audio_channel_representation_t outputRepresentation = + audio_channel_mask_get_representation(outputChannelMask); + const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask); + const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask); + + switch (inputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + switch (outputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry), + outputBits, inputBits); + return; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + // TODO: output channel index mask not currently allowed + // fall through + default: + break; + } + break; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + switch (outputRepresentation) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: + memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry), + outputBits, inputBits); + return; + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + // TODO: output channel index mask not currently allowed + // fall through + default: + break; + } + break; + default: + break; + } + LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x", + inputChannelMask, outputChannelMask); } void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames) @@ -430,6 +465,10 @@ void AudioMixer::setLog(NBLog::Writer *log) mState.mLog = log; } +static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) { + return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; +} + int AudioMixer::getTrackName(audio_channel_mask_t channelMask, audio_format_t format, int sessionId) { @@ -492,24 +531,23 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, t->mInputBufferProvider = NULL; t->mReformatBufferProvider = NULL; t->downmixerBufferProvider = NULL; + t->mPostDownmixReformatBufferProvider = NULL; t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; t->mFormat = format; - t->mMixerInFormat = kUseFloat && kUseNewMixer - ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + t->mMixerInFormat = selectMixerInFormat(format); + t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits( AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO); t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); // Check the downmixing (or upmixing) requirements. - status_t status = initTrackDownmix(t, n); + status_t status = t->prepareForDownmix(); if (status != OK) { ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); return -1; } - // initTrackDownmix() may change the input format requirement. - // If you desire floating point input to the mixer, it may change - // to integer because the downmixer requires integer to process. + // prepareForDownmix() may change mDownmixRequiresFormat ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); - prepareTrackForReformat(t, n); + t->prepareForReformat(); mTrackNames |= 1 << n; return TRACK0 + n; } @@ -526,7 +564,7 @@ void AudioMixer::invalidateState(uint32_t mask) } // Called when channel masks have changed for a track name -// TODO: Fix Downmixbufferprofider not to (possibly) change mixer input format, +// TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format, // which will simplify this logic. bool AudioMixer::setChannelMasks(int name, audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { @@ -551,21 +589,18 @@ bool AudioMixer::setChannelMasks(int name, // channel masks have changed, does this track need a downmixer? // update to try using our desired format (if we aren't already using it) - const audio_format_t prevMixerInFormat = track.mMixerInFormat; - track.mMixerInFormat = kUseFloat && kUseNewMixer - ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; - const status_t status = initTrackDownmix(&mState.tracks[name], name); + const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat; + const status_t status = mState.tracks[name].prepareForDownmix(); ALOGE_IF(status != OK, - "initTrackDownmix error %d, track channel mask %#x, mixer channel mask %#x", + "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x", status, track.channelMask, track.mMixerChannelMask); - const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat; - if (mixerInFormatChanged) { - prepareTrackForReformat(&track, name); // because of downmixer, track format may change! + if (prevDownmixerFormat != track.mDownmixRequiresFormat) { + track.prepareForReformat(); // because of downmixer, track format may change! } - if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) { - // resampler input format or channels may have changed. + if (track.resampler && mixerChannelCountChanged) { + // resampler channels may have changed. const uint32_t resetToSampleRate = track.sampleRate; delete track.resampler; track.resampler = NULL; @@ -576,99 +611,125 @@ bool AudioMixer::setChannelMasks(int name, return true; } -status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName) -{ - // Only remix (upmix or downmix) if the track and mixer/device channel masks - // are not the same and not handled internally, as mono -> stereo currently is. - if (pTrack->channelMask != pTrack->mMixerChannelMask - && !(pTrack->channelMask == AUDIO_CHANNEL_OUT_MONO - && pTrack->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { - return prepareTrackForDownmix(pTrack, trackName); - } - // no remix necessary - unprepareTrackForDownmix(pTrack, trackName); - return NO_ERROR; -} +void AudioMixer::track_t::unprepareForDownmix() { + ALOGV("AudioMixer::unprepareForDownmix(%p)", this); -void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) { - ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName); - - if (pTrack->downmixerBufferProvider != NULL) { + mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; + if (downmixerBufferProvider != NULL) { // this track had previously been configured with a downmixer, delete it ALOGV(" deleting old downmixer"); - delete pTrack->downmixerBufferProvider; - pTrack->downmixerBufferProvider = NULL; - reconfigureBufferProviders(pTrack); + delete downmixerBufferProvider; + downmixerBufferProvider = NULL; + reconfigureBufferProviders(); } else { ALOGV(" nothing to do, no downmixer to delete"); } } -status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) +status_t AudioMixer::track_t::prepareForDownmix() { - ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask); + ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x", + this, channelMask); // discard the previous downmixer if there was one - unprepareTrackForDownmix(pTrack, trackName); - if (DownmixerBufferProvider::isMultichannelCapable()) { - DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask, - pTrack->mMixerChannelMask, - AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */, - pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount); + unprepareForDownmix(); + // Only remix (upmix or downmix) if the track and mixer/device channel masks + // are not the same and not handled internally, as mono -> stereo currently is. + if (channelMask == mMixerChannelMask + || (channelMask == AUDIO_CHANNEL_OUT_MONO + && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { + return NO_ERROR; + } + // DownmixerBufferProvider is only used for position masks. + if (audio_channel_mask_get_representation(channelMask) + == AUDIO_CHANNEL_REPRESENTATION_POSITION + && DownmixerBufferProvider::isMultichannelCapable()) { + DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask, + mMixerChannelMask, + AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */, + sampleRate, sessionId, kCopyBufferFrameCount); if (pDbp->isValid()) { // if constructor completed properly - pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix - pTrack->downmixerBufferProvider = pDbp; - reconfigureBufferProviders(pTrack); + mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix + downmixerBufferProvider = pDbp; + reconfigureBufferProviders(); return NO_ERROR; } delete pDbp; } // Effect downmixer does not accept the channel conversion. Let's use our remixer. - RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask, - pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount); + RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask, + mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount); // Remix always finds a conversion whereas Downmixer effect above may fail. - pTrack->downmixerBufferProvider = pRbp; - reconfigureBufferProviders(pTrack); + downmixerBufferProvider = pRbp; + reconfigureBufferProviders(); return NO_ERROR; } -void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { - ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); - if (pTrack->mReformatBufferProvider != NULL) { - delete pTrack->mReformatBufferProvider; - pTrack->mReformatBufferProvider = NULL; - reconfigureBufferProviders(pTrack); +void AudioMixer::track_t::unprepareForReformat() { + ALOGV("AudioMixer::unprepareForReformat(%p)", this); + bool requiresReconfigure = false; + if (mReformatBufferProvider != NULL) { + delete mReformatBufferProvider; + mReformatBufferProvider = NULL; + requiresReconfigure = true; + } + if (mPostDownmixReformatBufferProvider != NULL) { + delete mPostDownmixReformatBufferProvider; + mPostDownmixReformatBufferProvider = NULL; + requiresReconfigure = true; + } + if (requiresReconfigure) { + reconfigureBufferProviders(); } } -status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName) +status_t AudioMixer::track_t::prepareForReformat() { - ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat); - // discard the previous reformatter if there was one - unprepareTrackForReformat(pTrack, trackName); - // only configure reformatter if needed - if (pTrack->mFormat != pTrack->mMixerInFormat) { - pTrack->mReformatBufferProvider = new ReformatBufferProvider( - audio_channel_count_from_out_mask(pTrack->channelMask), - pTrack->mFormat, pTrack->mMixerInFormat, + ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat); + // discard previous reformatters + unprepareForReformat(); + // only configure reformatters as needed + const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID + ? mDownmixRequiresFormat : mMixerInFormat; + bool requiresReconfigure = false; + if (mFormat != targetFormat) { + mReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(channelMask), + mFormat, + targetFormat, kCopyBufferFrameCount); - reconfigureBufferProviders(pTrack); + requiresReconfigure = true; + } + if (targetFormat != mMixerInFormat) { + mPostDownmixReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(mMixerChannelMask), + targetFormat, + mMixerInFormat, + kCopyBufferFrameCount); + requiresReconfigure = true; + } + if (requiresReconfigure) { + reconfigureBufferProviders(); } return NO_ERROR; } -void AudioMixer::reconfigureBufferProviders(track_t* pTrack) +void AudioMixer::track_t::reconfigureBufferProviders() { - pTrack->bufferProvider = pTrack->mInputBufferProvider; - if (pTrack->mReformatBufferProvider) { - pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider); - pTrack->bufferProvider = pTrack->mReformatBufferProvider; + bufferProvider = mInputBufferProvider; + if (mReformatBufferProvider) { + mReformatBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mReformatBufferProvider; + } + if (downmixerBufferProvider) { + downmixerBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = downmixerBufferProvider; } - if (pTrack->downmixerBufferProvider) { - pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider); - pTrack->bufferProvider = pTrack->downmixerBufferProvider; + if (mPostDownmixReformatBufferProvider) { + mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mPostDownmixReformatBufferProvider; } } @@ -687,9 +748,9 @@ void AudioMixer::deleteTrackName(int name) delete track.resampler; track.resampler = NULL; // delete the downmixer - unprepareTrackForDownmix(&mState.tracks[name], name); + mState.tracks[name].unprepareForDownmix(); // delete the reformatter - unprepareTrackForReformat(&mState.tracks[name], name); + mState.tracks[name].unprepareForReformat(); mTrackNames &= ~(1<<name); } @@ -828,7 +889,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); track.mFormat = format; ALOGV("setParameter(TRACK, FORMAT, %#x)", format); - prepareTrackForReformat(&track, name); + track.prepareForReformat(); invalidateState(1 << name); } } break; @@ -1032,10 +1093,13 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider if (mState.tracks[name].mReformatBufferProvider != NULL) { mState.tracks[name].mReformatBufferProvider->reset(); } else if (mState.tracks[name].downmixerBufferProvider != NULL) { + mState.tracks[name].downmixerBufferProvider->reset(); + } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) { + mState.tracks[name].mPostDownmixReformatBufferProvider->reset(); } mState.tracks[name].mInputBufferProvider = bufferProvider; - reconfigureBufferProviders(&mState.tracks[name]); + mState.tracks[name].reconfigureBufferProviders(); } @@ -2236,4 +2300,4 @@ AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t } // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index f4f142b..381036b 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -127,10 +127,16 @@ public: size_t getUnreleasedFrames(int name) const; static inline bool isValidPcmTrackFormat(audio_format_t format) { - return format == AUDIO_FORMAT_PCM_16_BIT || - format == AUDIO_FORMAT_PCM_24_BIT_PACKED || - format == AUDIO_FORMAT_PCM_32_BIT || - format == AUDIO_FORMAT_PCM_FLOAT; + switch (format) { + case AUDIO_FORMAT_PCM_8_BIT: + case AUDIO_FORMAT_PCM_16_BIT: + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_FLOAT: + return true; + default: + return false; + } } private: @@ -205,17 +211,34 @@ private: int32_t* auxBuffer; // 16-byte boundary + + /* Buffer providers are constructed to translate the track input data as needed. + * + * 1) mInputBufferProvider: The AudioTrack buffer provider. + * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to + * match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer + * requires reformat. For example, it may convert floating point input to + * PCM_16_bit if that's required by the downmixer. + * 3) downmixerBufferProvider: If not NULL, performs the channel remixing to match + * the number of channels required by the mixer sink. + * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from + * the downmixer requirements to the mixer engine input requirements. + */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. + CopyBufferProvider* mPostDownmixReformatBufferProvider; + // 16-byte boundary int32_t sessionId; - // 16-byte boundary audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) audio_format_t mFormat; // input track format audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) // each track must be converted to this format. + audio_format_t mDownmixRequiresFormat; // required downmixer format + // AUDIO_FORMAT_PCM_16_BIT if 16 bit necessary + // AUDIO_FORMAT_INVALID if no required format float mVolume[MAX_NUM_VOLUMES]; // floating point set volume float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume @@ -225,7 +248,6 @@ private: float mPrevAuxLevel; // floating point prev aux level float mAuxInc; // floating point aux increment - // 16-byte boundary audio_channel_mask_t mMixerChannelMask; uint32_t mMixerChannelCount; @@ -236,6 +258,12 @@ private: void adjustVolumeRamp(bool aux, bool useFloat = false); size_t getUnreleasedFrames() const { return resampler != NULL ? resampler->getUnreleasedFrames() : 0; }; + + status_t prepareForDownmix(); + void unprepareForDownmix(); + status_t prepareForReformat(); + void unprepareForReformat(); + void reconfigureBufferProviders(); }; typedef void (*process_hook_t)(state_t* state, int64_t pts); @@ -382,14 +410,6 @@ private: bool setChannelMasks(int name, audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); - // TODO: remove unused trackName/trackNum from functions below. - static status_t initTrackDownmix(track_t* pTrack, int trackName); - static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); - static void unprepareTrackForDownmix(track_t* pTrack, int trackName); - static status_t prepareTrackForReformat(track_t* pTrack, int trackNum); - static void unprepareTrackForReformat(track_t* pTrack, int trackName); - static void reconfigureBufferProviders(track_t* pTrack); - static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); @@ -465,6 +485,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_AUDIO_MIXER_H diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index 069d946..863614a 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -170,7 +170,6 @@ private: }; // ---------------------------------------------------------------------------- -} -; // namespace android +} // namespace android #endif // ANDROID_AUDIO_RESAMPLER_H diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp index 8f14ff9..d3cbd1c 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -185,5 +185,4 @@ save_state: } // ---------------------------------------------------------------------------- -} -; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h index b315da5..1ddc5f9 100644 --- a/services/audioflinger/AudioResamplerCubic.h +++ b/services/audioflinger/AudioResamplerCubic.h @@ -63,6 +63,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/ diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp index 0eeb201..c21d4ca 100644 --- a/services/audioflinger/AudioResamplerDyn.cpp +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -618,4 +618,4 @@ template class AudioResamplerDyn<int16_t, int16_t, int32_t>; template class AudioResamplerDyn<int32_t, int16_t, int32_t>; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h index e886a68..238b163 100644 --- a/services/audioflinger/AudioResamplerDyn.h +++ b/services/audioflinger/AudioResamplerDyn.h @@ -127,6 +127,6 @@ private: void* mCoefBuffer; // if a filter is created, this is not null }; -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_DYN_H*/ diff --git a/services/audioflinger/AudioResamplerFirGen.h b/services/audioflinger/AudioResamplerFirGen.h index f3718b6..ad18965 100644 --- a/services/audioflinger/AudioResamplerFirGen.h +++ b/services/audioflinger/AudioResamplerFirGen.h @@ -204,7 +204,8 @@ struct I0ATerm { template <> struct I0ATerm<0> { // 1/sqrt(2*PI); - static const CONSTEXPR double value = 0.398942280401432677939946059934381868475858631164934657665925; + static const CONSTEXPR double value = + 0.398942280401432677939946059934381868475858631164934657665925; }; #if USE_HORNERS_METHOD @@ -706,6 +707,6 @@ static inline void firKaiserGen(T* coef, int L, int halfNumCoef, } } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_GEN_H*/ diff --git a/services/audioflinger/AudioResamplerFirOps.h b/services/audioflinger/AudioResamplerFirOps.h index bf2163f..658285d 100644 --- a/services/audioflinger/AudioResamplerFirOps.h +++ b/services/audioflinger/AudioResamplerFirOps.h @@ -25,7 +25,7 @@ namespace android { #define USE_INLINE_ASSEMBLY (false) #endif -#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__) +#if defined(__aarch64__) || defined(__ARM_NEON__) #define USE_NEON (true) #include <arm_neon.h> #else @@ -158,6 +158,6 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) #endif } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_OPS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h index efc8055..176202e 100644 --- a/services/audioflinger/AudioResamplerFirProcess.h +++ b/services/audioflinger/AudioResamplerFirProcess.h @@ -174,7 +174,8 @@ struct InterpNull { * Process() calls ProcessBase() with TFUNC = InterpCompute, for interpolated phase. */ -template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, typename TINTERP> +template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, + typename TINTERP> static inline void ProcessBase(TO* const out, size_t count, @@ -242,6 +243,9 @@ void ProcessBase(TO* const out, } } +/* Calculates a single output frame from a polyphase resampling filter. + * See Process() for parameter details. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO> static inline void ProcessL(TO* const out, @@ -255,6 +259,39 @@ void ProcessL(TO* const out, ProcessBase<CHANNELS, STRIDE, InterpNull>(out, count, coefsP, coefsN, sP, sN, 0, volumeLR); } +/* + * Calculates a single output frame from a polyphase resampling filter, + * with filter phase interpolation. + * + * @param out should point to the output buffer with space for at least one output frame. + * + * @param count should be half the size of the total filter length (halfNumCoefs), as we + * use symmetry in filter coefficients to evaluate two dot products. + * + * @param coefsP is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the positive sP. + * + * @param coefsN is one phase of the polyphase filter bank of size halfNumCoefs, corresponding + * to the negative sN. + * + * @param coefsP1 is the next phase of coefsP (used for interpolation). + * + * @param coefsN1 is the next phase of coefsN (used for interpolation). + * + * @param sP is the positive half of the coefficients (as viewed by a convolution), + * starting at the original samples pointer and decrementing (by CHANNELS). + * + * @param sN is the negative half of the samples (as viewed by a convolution), + * starting at the original samples pointer + CHANNELS and incrementing (by CHANNELS). + * + * @param lerpP The fractional siting between the polyphase indices is given by the bits + * below coefShift. See fir() for details. + * + * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, + * expressed as a S32 integer or float. A negative value inverts the channel 180 degrees. + * The pointer volumeLR should be aligned to a minimum of 8 bytes. + * A typical value for volume is 0x1000 to align to a unity gain output of 20.12. + */ template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP> static inline void Process(TO* const out, @@ -268,11 +305,12 @@ void Process(TO* const out, TINTERP lerpP, const TO* const volumeLR) { - ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR); + ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, + volumeLR); } /* - * Calculates a single output frame (two samples) from input sample pointer. + * Calculates a single output frame from input sample pointer. * * This sets up the params for the accelerated Process() and ProcessL() * functions to do the appropriate dot products. @@ -307,7 +345,7 @@ void Process(TO* const out, * the positive half of the filter is dot product from samples to samples-halfNumCoefs+1. * * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, - * expressed as a S32 integer. A negative value inverts the channel 180 degrees. + * expressed as a S32 integer or float. A negative value inverts the channel 180 degrees. * The pointer volumeLR should be aligned to a minimum of 8 bytes. * A typical value for volume is 0x1000 to align to a unity gain output of 20.12. * @@ -396,6 +434,6 @@ void fir(TO* const out, } } -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcessNeon.h b/services/audioflinger/AudioResamplerFirProcessNeon.h index f311cef..3de9edd 100644 --- a/services/audioflinger/AudioResamplerFirProcessNeon.h +++ b/services/audioflinger/AudioResamplerFirProcessNeon.h @@ -22,14 +22,35 @@ namespace android { // depends on AudioResamplerFirOps.h, AudioResamplerFirProcess.h #if USE_NEON + +// use intrinsics if inline arm32 assembly is not possible +#if !USE_INLINE_ASSEMBLY +#define USE_INTRINSIC +#endif + +// following intrinsics available only on ARM 64 bit ACLE +#ifndef __aarch64__ +#undef vld1q_f32_x2 +#undef vld1q_s32_x2 +#endif + +#define TO_STRING2(x) #x +#define TO_STRING(x) TO_STRING2(x) +// uncomment to print GCC version, may be relevant for intrinsic optimizations +/* #pragma message ("GCC version: " TO_STRING(__GNUC__) \ + "." TO_STRING(__GNUC_MINOR__) \ + "." TO_STRING(__GNUC_PATCHLEVEL__)) */ + // -// NEON specializations are enabled for Process() and ProcessL() +// NEON specializations are enabled for Process() and ProcessL() in AudioResamplerFirProcess.h +// +// Two variants are presented here: +// ARM NEON inline assembly which appears up to 10-15% faster than intrinsics (gcc 4.9) for arm32. +// ARM NEON intrinsics which can also be used by arm64 and x86/64 with NEON header. // -// TODO: Stride 16 and Stride 8 can be combined with one pass stride 8 (if necessary) -// and looping stride 16 (or vice versa). This has some polyphase coef data alignment -// issues with S16 coefs. Consider this later. // Macros to save a mono/stereo accumulator sample in q0 (and q4) as stereo out. +// These are only used for inline assembly. #define ASSEMBLY_ACCUMULATE_MONO \ "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes */\ "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output */\ @@ -49,6 +70,458 @@ namespace android { "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating)*/\ "vst1.s32 {d3}, %[out] \n"/* (2+2d)store result*/ +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int16_t* coefsP1, + const int16_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int16_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int16_t*)__builtin_assume_aligned(coefsN, 16); + + int16x4_t interp; + if (!FIXED) { + interp = vdup_n_s16(lerpP); + //interp = (int16x4_t)vset_lane_s32 ((int32x2_t)lerpP, interp, 0); + coefsP1 = (const int16_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int16_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { + int16x8_t posCoef = vld1q_s16(coefsP); + coefsP += 8; + int16x8_t negCoef = vld1q_s16(coefsN); + coefsN += 8; + if (!FIXED) { // interpolate + int16x8_t posCoef1 = vld1q_s16(coefsP1); + coefsP1 += 8; + int16x8_t negCoef1 = vld1q_s16(coefsN1); + coefsN1 += 8; + + posCoef1 = vsubq_s16(posCoef1, posCoef); + negCoef = vsubq_s16(negCoef, negCoef1); + + posCoef1 = vqrdmulhq_lane_s16(posCoef1, interp, 0); + negCoef = vqrdmulhq_lane_s16(negCoef, interp, 0); + + posCoef = vaddq_s16(posCoef, posCoef1); + negCoef = vaddq_s16(negCoef, negCoef1); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp), vget_high_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_high_s16(posSamp), vget_low_s16(posCoef)); // reversed + accum = vmlal_s16(accum, vget_low_s16(negSamp), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp), vget_high_s16(negCoef)); + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // dot product + accum = vmlal_s16(accum, vget_low_s16(posSamp.val[0]), vget_high_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_high_s16(posSamp.val[0]), vget_low_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_low_s16(posSamp.val[1]), vget_high_s16(posCoef)); // r + accum2 = vmlal_s16(accum2, vget_high_s16(posSamp.val[1]), vget_low_s16(posCoef)); // r + accum = vmlal_s16(accum, vget_low_s16(negSamp.val[0]), vget_low_s16(negCoef)); + accum = vmlal_s16(accum, vget_high_s16(negSamp.val[0]), vget_high_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_low_s16(negSamp.val[1]), vget_low_s16(negCoef)); + accum2 = vmlal_s16(accum2, vget_high_s16(negSamp.val[1]), vget_high_s16(negCoef)); + sP -= 16; + } + } break; + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(int32_t* out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* volumeLR, + uint32_t lerpP, + const int32_t* coefsP1, + const int32_t* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const int32_t*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const int32_t*)__builtin_assume_aligned(coefsN, 16); + + int32x2_t interp; + if (!FIXED) { + interp = vdup_n_s32(lerpP); + coefsP1 = (const int32_t*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const int32_t*)__builtin_assume_aligned(coefsN1, 16); + } + int32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_s32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_s32(0); + } + do { +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef = vld1q_s32_x2(coefsP); + coefsP += 8; + int32x4x2_t negCoef = vld1q_s32_x2(coefsN); + coefsN += 8; +#else + int32x4x2_t posCoef; + posCoef.val[0] = vld1q_s32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_s32(coefsP); + coefsP += 4; + int32x4x2_t negCoef; + negCoef.val[0] = vld1q_s32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_s32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_s32_x2 + int32x4x2_t posCoef1 = vld1q_s32_x2(coefsP1); + coefsP1 += 8; + int32x4x2_t negCoef1 = vld1q_s32_x2(coefsN1); + coefsN1 += 8; +#else + int32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_s32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_s32(coefsP1); + coefsP1 += 4; + int32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_s32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_s32(coefsN1); + coefsN1 += 4; +#endif + + posCoef1.val[0] = vsubq_s32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_s32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_s32(negCoef.val[1], negCoef1.val[1]); + + posCoef1.val[0] = vqrdmulhq_lane_s32(posCoef1.val[0], interp, 0); + posCoef1.val[1] = vqrdmulhq_lane_s32(posCoef1.val[1], interp, 0); + negCoef.val[0] = vqrdmulhq_lane_s32(negCoef.val[0], interp, 0); + negCoef.val[1] = vqrdmulhq_lane_s32(negCoef.val[1], interp, 0); + + posCoef.val[0] = vaddq_s32(posCoef.val[0], posCoef1.val[0]); + posCoef.val[1] = vaddq_s32(posCoef.val[1], posCoef1.val[1]); + negCoef.val[0] = vaddq_s32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vaddq_s32(negCoef.val[1], negCoef1.val[1]); + } + switch (CHANNELS) { + case 1: { + int16x8_t posSamp = vld1q_s16(sP); + int16x8_t negSamp = vld1q_s16(sN); + sN += 8; + posSamp = vrev64q_s16(posSamp); + + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + sP -= 8; + } break; + case 2: { + int16x8x2_t posSamp = vld2q_s16(sP); + int16x8x2_t negSamp = vld2q_s16(sN); + sN += 16; + posSamp.val[0] = vrev64q_s16(posSamp.val[0]); + posSamp.val[1] = vrev64q_s16(posSamp.val[1]); + + // left + int32x4_t posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[0]), 15); + int32x4_t posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[0]), 15); + int32x4_t negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[0]), 15); + int32x4_t negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[0]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum = vaddq_s32(accum, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum = vaddq_s32(accum, posSamp1); + accum = vaddq_s32(accum, negSamp0); + + // right + posSamp0 = vshll_n_s16(vget_low_s16(posSamp.val[1]), 15); + posSamp1 = vshll_n_s16(vget_high_s16(posSamp.val[1]), 15); + negSamp0 = vshll_n_s16(vget_low_s16(negSamp.val[1]), 15); + negSamp1 = vshll_n_s16(vget_high_s16(negSamp.val[1]), 15); + + // dot product + posSamp0 = vqrdmulhq_s32(posSamp0, posCoef.val[1]); // reversed + posSamp1 = vqrdmulhq_s32(posSamp1, posCoef.val[0]); // reversed + negSamp0 = vqrdmulhq_s32(negSamp0, negCoef.val[0]); + negSamp1 = vqrdmulhq_s32(negSamp1, negCoef.val[1]); + + accum2 = vaddq_s32(accum2, posSamp0); + negSamp0 = vaddq_s32(negSamp0, negSamp1); + accum2 = vaddq_s32(accum2, posSamp1); + accum2 = vaddq_s32(accum2, negSamp0); + + sP -= 16; + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const int32_t*)__builtin_assume_aligned(volumeLR, 8); + int32x2_t vLR = vld1_s32(volumeLR); + int32x2_t outSamp = vld1_s32(out); + // combine and funnel down accumulator + int32x2_t outAccum = vpadd_s32(vget_low_s32(accum), vget_high_s32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_s32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + int32x2_t outAccum2 = vpadd_s32(vget_low_s32(accum2), vget_high_s32(accum2)); + outAccum = vpadd_s32(outAccum, outAccum2); + } + outAccum = vqrdmulh_s32(outAccum, vLR); + outSamp = vqadd_s32(outSamp, outAccum); + vst1_s32(out, outSamp); +} + +template <int CHANNELS, int STRIDE, bool FIXED> +static inline void ProcessNeonIntrinsic(float* out, + int count, + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* volumeLR, + float lerpP, + const float* coefsP1, + const float* coefsN1) +{ + ALOG_ASSERT(count > 0 && (count & 7) == 0); // multiple of 8 + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS == 1 || CHANNELS == 2); + + sP -= CHANNELS*((STRIDE>>1)-1); + coefsP = (const float*)__builtin_assume_aligned(coefsP, 16); + coefsN = (const float*)__builtin_assume_aligned(coefsN, 16); + + float32x2_t interp; + if (!FIXED) { + interp = vdup_n_f32(lerpP); + coefsP1 = (const float*)__builtin_assume_aligned(coefsP1, 16); + coefsN1 = (const float*)__builtin_assume_aligned(coefsN1, 16); + } + float32x4_t accum, accum2; + // warning uninitialized if we use veorq_s32 + // (alternative to below) accum = veorq_s32(accum, accum); + accum = vdupq_n_f32(0); + if (CHANNELS == 2) { + // (alternative to below) accum2 = veorq_s32(accum2, accum2); + accum2 = vdupq_n_f32(0); + } + do { +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef = vld1q_f32_x2(coefsP); + coefsP += 8; + float32x4x2_t negCoef = vld1q_f32_x2(coefsN); + coefsN += 8; +#else + float32x4x2_t posCoef; + posCoef.val[0] = vld1q_f32(coefsP); + coefsP += 4; + posCoef.val[1] = vld1q_f32(coefsP); + coefsP += 4; + float32x4x2_t negCoef; + negCoef.val[0] = vld1q_f32(coefsN); + coefsN += 4; + negCoef.val[1] = vld1q_f32(coefsN); + coefsN += 4; +#endif + if (!FIXED) { // interpolate +#ifdef vld1q_f32_x2 + float32x4x2_t posCoef1 = vld1q_f32_x2(coefsP1); + coefsP1 += 8; + float32x4x2_t negCoef1 = vld1q_f32_x2(coefsN1); + coefsN1 += 8; +#else + float32x4x2_t posCoef1; + posCoef1.val[0] = vld1q_f32(coefsP1); + coefsP1 += 4; + posCoef1.val[1] = vld1q_f32(coefsP1); + coefsP1 += 4; + float32x4x2_t negCoef1; + negCoef1.val[0] = vld1q_f32(coefsN1); + coefsN1 += 4; + negCoef1.val[1] = vld1q_f32(coefsN1); + coefsN1 += 4; +#endif + posCoef1.val[0] = vsubq_f32(posCoef1.val[0], posCoef.val[0]); + posCoef1.val[1] = vsubq_f32(posCoef1.val[1], posCoef.val[1]); + negCoef.val[0] = vsubq_f32(negCoef.val[0], negCoef1.val[0]); + negCoef.val[1] = vsubq_f32(negCoef.val[1], negCoef1.val[1]); + + posCoef.val[0] = vmlaq_lane_f32(posCoef.val[0], posCoef1.val[0], interp, 0); + posCoef.val[1] = vmlaq_lane_f32(posCoef.val[1], posCoef1.val[1], interp, 0); + negCoef.val[0] = vmlaq_lane_f32(negCoef1.val[0], negCoef.val[0], interp, 0); // rev + negCoef.val[1] = vmlaq_lane_f32(negCoef1.val[1], negCoef.val[1], interp, 0); // rev + } + switch (CHANNELS) { + case 1: { +#ifdef vld1q_f32_x2 + float32x4x2_t posSamp = vld1q_f32_x2(sP); + float32x4x2_t negSamp = vld1q_f32_x2(sN); + sN += 8; + sP -= 8; +#else + float32x4x2_t posSamp; + posSamp.val[0] = vld1q_f32(sP); + sP += 4; + posSamp.val[1] = vld1q_f32(sP); + sP -= 12; + float32x4x2_t negSamp; + negSamp.val[0] = vld1q_f32(sN); + sN += 4; + negSamp.val[1] = vld1q_f32(sN); + sN += 4; +#endif + // effectively we want a vrev128q_f32() + posSamp.val[0] = vrev64q_f32(posSamp.val[0]); + posSamp.val[1] = vrev64q_f32(posSamp.val[1]); + posSamp.val[0] = vcombine_f32( + vget_high_f32(posSamp.val[0]), vget_low_f32(posSamp.val[0])); + posSamp.val[1] = vcombine_f32( + vget_high_f32(posSamp.val[1]), vget_low_f32(posSamp.val[1])); + + accum = vmlaq_f32(accum, posSamp.val[0], posCoef.val[1]); + accum = vmlaq_f32(accum, posSamp.val[1], posCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp.val[1], negCoef.val[1]); + } break; + case 2: { + float32x4x2_t posSamp0 = vld2q_f32(sP); + sP += 8; + float32x4x2_t negSamp0 = vld2q_f32(sN); + sN += 8; + posSamp0.val[0] = vrev64q_f32(posSamp0.val[0]); + posSamp0.val[1] = vrev64q_f32(posSamp0.val[1]); + posSamp0.val[0] = vcombine_f32( + vget_high_f32(posSamp0.val[0]), vget_low_f32(posSamp0.val[0])); + posSamp0.val[1] = vcombine_f32( + vget_high_f32(posSamp0.val[1]), vget_low_f32(posSamp0.val[1])); + + float32x4x2_t posSamp1 = vld2q_f32(sP); + sP -= 24; + float32x4x2_t negSamp1 = vld2q_f32(sN); + sN += 8; + posSamp1.val[0] = vrev64q_f32(posSamp1.val[0]); + posSamp1.val[1] = vrev64q_f32(posSamp1.val[1]); + posSamp1.val[0] = vcombine_f32( + vget_high_f32(posSamp1.val[0]), vget_low_f32(posSamp1.val[0])); + posSamp1.val[1] = vcombine_f32( + vget_high_f32(posSamp1.val[1]), vget_low_f32(posSamp1.val[1])); + + // Note: speed is affected by accumulation order. + // Also, speed appears slower using vmul/vadd instead of vmla for + // stereo case, comparable for mono. + + accum = vmlaq_f32(accum, negSamp0.val[0], negCoef.val[0]); + accum = vmlaq_f32(accum, negSamp1.val[0], negCoef.val[1]); + accum2 = vmlaq_f32(accum2, negSamp0.val[1], negCoef.val[0]); + accum2 = vmlaq_f32(accum2, negSamp1.val[1], negCoef.val[1]); + + accum = vmlaq_f32(accum, posSamp0.val[0], posCoef.val[1]); // reversed + accum = vmlaq_f32(accum, posSamp1.val[0], posCoef.val[0]); // reversed + accum2 = vmlaq_f32(accum2, posSamp0.val[1], posCoef.val[1]); // reversed + accum2 = vmlaq_f32(accum2, posSamp1.val[1], posCoef.val[0]); // reversed + } break; + } + } while (count -= 8); + + // multiply by volume and save + volumeLR = (const float*)__builtin_assume_aligned(volumeLR, 8); + float32x2_t vLR = vld1_f32(volumeLR); + float32x2_t outSamp = vld1_f32(out); + // combine and funnel down accumulator + float32x2_t outAccum = vpadd_f32(vget_low_f32(accum), vget_high_f32(accum)); + if (CHANNELS == 1) { + // duplicate accum to both L and R + outAccum = vpadd_f32(outAccum, outAccum); + } else if (CHANNELS == 2) { + // accum2 contains R, fold in + float32x2_t outAccum2 = vpadd_f32(vget_low_f32(accum2), vget_high_f32(accum2)); + outAccum = vpadd_f32(outAccum, outAccum2); + } + outSamp = vmla_f32(outSamp, outAccum, vLR); + vst1_f32(out, outSamp); +} + template <> inline void ProcessL<1, 16>(int32_t* const out, int count, @@ -58,6 +531,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -99,6 +576,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q10" ); +#endif } template <> @@ -110,6 +588,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -119,13 +601,13 @@ inline void ProcessL<2, 16>(int32_t* const out, "1: \n" - "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples - "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo frames "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs "vld1.16 {q10}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs - "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive - "vrev64.16 q3, q3 \n"// (0 combines+) reverse right positive + "vrev64.16 q2, q2 \n"// (1) reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// (0 combines+) reverse positive right "vmlal.s16 q0, d4, d17 \n"// (1) multiply (reversed) samples left "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed) samples left @@ -157,6 +639,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q10" ); +#endif } template <> @@ -171,6 +654,11 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else + const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -227,6 +715,7 @@ inline void Process<1, 16>(int32_t* const out, "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -241,6 +730,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -251,8 +744,8 @@ inline void Process<2, 16>(int32_t* const out, "1: \n" - "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples - "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo frames "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs "vld1.16 {q9}, [%[coefsP1]:128]! \n"// (1) load 8 16-bits coefs for interpolation "vld1.16 {q10}, [%[coefsN1]:128]! \n"// (1) load 8 16-bits coefs @@ -264,8 +757,8 @@ inline void Process<2, 16>(int32_t* const out, "vqrdmulh.s16 q9, q9, d2[0] \n"// (2) interpolate (step2) 1st set of coefs "vqrdmulh.s16 q11, q11, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs - "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive - "vrev64.16 q3, q3 \n"// (1) reverse 8 frames of the right positive + "vrev64.16 q2, q2 \n"// (1) reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// (1) reverse 8 samples of positive right "vadd.s16 q8, q8, q9 \n"// (1+1d) interpolate (step3) 1st set "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set @@ -303,6 +796,7 @@ inline void Process<2, 16>(int32_t* const out, "q4", "q5", "q6", "q8", "q9", "q10", "q11" ); +#endif } template <> @@ -314,6 +808,10 @@ inline void ProcessL<1, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -327,7 +825,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs - "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + "vrev64.16 q2, q2 \n"// reverse 8 samples of the positive side "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -335,10 +833,10 @@ inline void ProcessL<1, 16>(int32_t* const out, "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits - "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples "vadd.s32 q0, q0, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result @@ -364,6 +862,7 @@ inline void ProcessL<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -375,6 +874,10 @@ inline void ProcessL<2, 16>(int32_t* const out, const int16_t* sN, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -384,13 +887,13 @@ inline void ProcessL<2, 16>(int32_t* const out, "1: \n" - "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples - "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples - "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs + "vld2.16 {q2, q3}, [%[sP]] \n"// load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 8 16-bits stereo frames + "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs - "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side - "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side + "vrev64.16 q2, q2 \n"// reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// reverse 8 samples of positive right "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -398,15 +901,15 @@ inline void ProcessL<2, 16>(int32_t* const out, "vshll.s16 q14, d10, #15 \n"// extend samples to 31 bits "vshll.s16 q15, d11, #15 \n"// extend samples to 31 bits - "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by coef "vadd.s32 q0, q0, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result - "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result - "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits @@ -414,15 +917,15 @@ inline void ProcessL<2, 16>(int32_t* const out, "vshll.s16 q14, d12, #15 \n"// extend samples to 31 bits "vshll.s16 q15, d13, #15 \n"// extend samples to 31 bits - "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by coef "vadd.s32 q4, q4, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result - "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result - "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result + "vadd.s32 q4, q4, q15 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result "subs %[count], %[count], #8 \n"// update loop counter "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples @@ -444,6 +947,7 @@ inline void ProcessL<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -458,6 +962,10 @@ inline void Process<1, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 1; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -489,7 +997,7 @@ inline void Process<1, 16>(int32_t* const out, "vadd.s32 q10, q10, q14 \n"// interpolate (step3) "vadd.s32 q11, q11, q15 \n"// interpolate (step3) - "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + "vrev64.16 q2, q2 \n"// reverse 8 samples of the positive side "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -529,6 +1037,7 @@ inline void Process<1, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } template <> @@ -543,6 +1052,10 @@ inline void Process<2, 16>(int32_t* const out, uint32_t lerpP, const int32_t* const volumeLR) { +#ifdef USE_INTRINSIC + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); +#else const int CHANNELS = 2; // template specialization does not preserve params const int STRIDE = 16; sP -= CHANNELS*((STRIDE>>1)-1); @@ -553,8 +1066,8 @@ inline void Process<2, 16>(int32_t* const out, "1: \n" - "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples - "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples + "vld2.16 {q2, q3}, [%[sP]] \n"// load 8 16-bits stereo frames + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 8 16-bits stereo frames "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs "vld1.32 {q12, q13}, [%[coefsP1]:128]! \n"// load 8 32-bits coefs "vld1.32 {q10, q11}, [%[coefsN1]:128]! \n"// load 8 32-bits coefs @@ -575,8 +1088,8 @@ inline void Process<2, 16>(int32_t* const out, "vadd.s32 q10, q10, q14 \n"// interpolate (step3) "vadd.s32 q11, q11, q15 \n"// interpolate (step3) - "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side - "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side + "vrev64.16 q2, q2 \n"// reverse 8 samples of positive left + "vrev64.16 q3, q3 \n"// reverse 8 samples of positive right "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits @@ -591,8 +1104,8 @@ inline void Process<2, 16>(int32_t* const out, "vadd.s32 q0, q0, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result - "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result - "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits @@ -607,8 +1120,8 @@ inline void Process<2, 16>(int32_t* const out, "vadd.s32 q4, q4, q12 \n"// accumulate result "vadd.s32 q13, q13, q14 \n"// accumulate result - "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result - "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result + "vadd.s32 q4, q4, q15 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result "subs %[count], %[count], #8 \n"// update loop counter "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples @@ -633,517 +1146,69 @@ inline void Process<2, 16>(int32_t* const out, "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15" ); +#endif } -template <> -inline void ProcessL<1, 8>(int32_t* const out, +template<> +inline void ProcessL<1, 16>(float* const out, int count, - const int16_t* coefsP, - const int16_t* coefsN, - const int16_t* sP, - const int16_t* sN, - const int32_t* const volumeLR) + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* const volumeLR) { - const int CHANNELS = 1; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 - - "1: \n" - - "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples - "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples - "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs - "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs - - "vrev64.16 d4, d4 \n"// (1) reversed s3, s2, s1, s0, s7, s6, s5, s4 - - // reordering the vmal to do d6, d7 before d4, d5 is slower(?) - "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed)samples by coef - "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples - - // moving these ARM instructions before neon above seems to be slower - "subs %[count], %[count], #4 \n"// (1) update loop counter - "sub %[sP], %[sP], #8 \n"// (0) move pointer to next set of samples - - // sP used after branch (warning) - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_MONO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q8", "q10" - ); + ProcessNeonIntrinsic<1, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); } -template <> -inline void ProcessL<2, 8>(int32_t* const out, +template<> +inline void ProcessL<2, 16>(float* const out, int count, - const int16_t* coefsP, - const int16_t* coefsN, - const int16_t* sP, - const int16_t* sN, - const int32_t* const volumeLR) + const float* coefsP, + const float* coefsN, + const float* sP, + const float* sN, + const float* const volumeLR) { - const int CHANNELS = 2; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "veor q0, q0, q0 \n"// (1) acc_L = 0 - "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 - - "1: \n" - - "vld2.16 {d4, d5}, [%[sP]] \n"// (2+0d) load 8 16-bits stereo samples - "vld2.16 {d6, d7}, [%[sN]]! \n"// (2) load 8 16-bits stereo samples - "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs - "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs - - "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive - - "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left - "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right - "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left - "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right - - // moving these ARM before neon seems to be slower - "subs %[count], %[count], #4 \n"// (1) update loop counter - "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples - - // sP used after branch (warning) - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_STEREO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q4", "q5", "q6", - "q8", "q10" - ); + ProcessNeonIntrinsic<2, 16, true>(out, count, coefsP, coefsN, sP, sN, volumeLR, + 0 /*lerpP*/, NULL /*coefsP1*/, NULL /*coefsN1*/); } -template <> -inline void Process<1, 8>(int32_t* const out, +template<> +inline void Process<1, 16>(float* const out, int count, - const int16_t* coefsP, - const int16_t* coefsN, - const int16_t* coefsP1, - const int16_t* coefsN1, - const int16_t* sP, - const int16_t* sN, - uint32_t lerpP, - const int32_t* const volumeLR) + const float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* const volumeLR) { - const int CHANNELS = 1; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "vmov.32 d2[0], %[lerpP] \n"// load the positive phase S32 Q15 - "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 - - "1: \n" - - "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples - "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples - "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs - "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 4 16-bits coefs for interpolation - "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 4 16-bits coefs - "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs for interpolation - - "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs - "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets - - "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs - "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs - - "vrev64.16 d4, d4 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4 - - "vadd.s16 d16, d16, d17 \n"// (1+2d) interpolate (step3) 1st set - "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set - - // reordering the vmal to do d6, d7 before d4, d5 is slower(?) - "vmlal.s16 q0, d4, d16 \n"// (1+0d) multiply (reversed)by coef - "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples - - // moving these ARM instructions before neon above seems to be slower - "subs %[count], %[count], #4 \n"// (1) update loop counter - "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples - - // sP used after branch (warning) - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_MONO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [coefsP1] "+r" (coefsP1), - [coefsN1] "+r" (coefsN1), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [lerpP] "r" (lerpP), - [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q8", "q9", "q10", "q11" - ); + ProcessNeonIntrinsic<1, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); } -template <> -inline void Process<2, 8>(int32_t* const out, +template<> +inline void Process<2, 16>(float* const out, int count, - const int16_t* coefsP, - const int16_t* coefsN, - const int16_t* coefsP1, - const int16_t* coefsN1, - const int16_t* sP, - const int16_t* sN, - uint32_t lerpP, - const int32_t* const volumeLR) + const float* coefsP, + const float* coefsN, + const float* coefsP1, + const float* coefsN1, + const float* sP, + const float* sN, + float lerpP, + const float* const volumeLR) { - const int CHANNELS = 2; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "vmov.32 d2[0], %[lerpP] \n"// load the positive phase - "veor q0, q0, q0 \n"// (1) acc_L = 0 - "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 - - "1: \n" - - "vld2.16 {d4, d5}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples - "vld2.16 {d6, d7}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples - "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs - "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 8 16-bits coefs for interpolation - "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 8 16-bits coefs - "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs for interpolation - - "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs - "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets - - "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs - "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs - - "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive - - "vadd.s16 d16, d16, d17 \n"// (1+1d) interpolate (step3) 1st set - "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set - - "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left - "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right - "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left - "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right - - // moving these ARM before neon seems to be slower - "subs %[count], %[count], #4 \n"// (1) update loop counter - "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples - - // sP used after branch (warning) - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_STEREO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [coefsP1] "+r" (coefsP1), - [coefsN1] "+r" (coefsN1), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [lerpP] "r" (lerpP), - [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q4", "q5", "q6", - "q8", "q9", "q10", "q11" - ); -} - -template <> -inline void ProcessL<1, 8>(int32_t* const out, - int count, - const int32_t* coefsP, - const int32_t* coefsN, - const int16_t* sP, - const int16_t* sN, - const int32_t* const volumeLR) -{ - const int CHANNELS = 1; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "veor q0, q0, q0 \n"// result, initialize to 0 - - "1: \n" - - "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples - "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples - "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs - - "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side - - "vshll.s16 q12, d4, #15 \n"// (stall) extend samples to 31 bits - "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits - - "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - - "vadd.s32 q0, q0, q12 \n"// accumulate result - "vadd.s32 q0, q0, q14 \n"// (stall) accumulate result - - "subs %[count], %[count], #4 \n"// update loop counter - "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples - - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_MONO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q8", "q9", "q10", "q11", - "q12", "q14" - ); -} - -template <> -inline void ProcessL<2, 8>(int32_t* const out, - int count, - const int32_t* coefsP, - const int32_t* coefsN, - const int16_t* sP, - const int16_t* sN, - const int32_t* const volumeLR) -{ - const int CHANNELS = 2; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "veor q0, q0, q0 \n"// result, initialize to 0 - "veor q4, q4, q4 \n"// result, initialize to 0 - - "1: \n" - - "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples - "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples - "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs - - "vrev64.16 q2, q2 \n"// reverse 2 frames of the positive side - - "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits - "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits - - "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits - "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits - - "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by coef - "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by coef - "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by coef - - "vadd.s32 q0, q0, q12 \n"// accumulate result - "vadd.s32 q4, q4, q13 \n"// accumulate result - "vadd.s32 q0, q0, q14 \n"// accumulate result - "vadd.s32 q4, q4, q15 \n"// accumulate result - - "subs %[count], %[count], #4 \n"// update loop counter - "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples - - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_STEREO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsN0] "+r" (coefsN), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", "q4", - "q8", "q9", "q10", "q11", - "q12", "q13", "q14", "q15" - ); -} - -template <> -inline void Process<1, 8>(int32_t* const out, - int count, - const int32_t* coefsP, - const int32_t* coefsN, - const int32_t* coefsP1, - const int32_t* coefsN1, - const int16_t* sP, - const int16_t* sN, - uint32_t lerpP, - const int32_t* const volumeLR) -{ - const int CHANNELS = 1; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "vmov.32 d2[0], %[lerpP] \n"// load the positive phase - "veor q0, q0, q0 \n"// result, initialize to 0 - - "1: \n" - - "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples - "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples - "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation - "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation - - "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side - - "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs - "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets - "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits - - "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs - "vqrdmulh.s32 q11, q11, d2[0] \n"// interpolate (step2) 2nd set of coefs - "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits - - "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set - "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set - - "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - - "vadd.s32 q0, q0, q12 \n"// accumulate result - "vadd.s32 q0, q0, q14 \n"// accumulate result - - "subs %[count], %[count], #4 \n"// update loop counter - "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples - - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_MONO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsP1] "+r" (coefsP1), - [coefsN0] "+r" (coefsN), - [coefsN1] "+r" (coefsN1), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [lerpP] "r" (lerpP), - [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", - "q8", "q9", "q10", "q11", - "q12", "q14" - ); -} - -template <> -inline -void Process<2, 8>(int32_t* const out, - int count, - const int32_t* coefsP, - const int32_t* coefsN, - const int32_t* coefsP1, - const int32_t* coefsN1, - const int16_t* sP, - const int16_t* sN, - uint32_t lerpP, - const int32_t* const volumeLR) -{ - const int CHANNELS = 2; // template specialization does not preserve params - const int STRIDE = 8; - sP -= CHANNELS*((STRIDE>>1)-1); - asm ( - "vmov.32 d2[0], %[lerpP] \n"// load the positive phase - "veor q0, q0, q0 \n"// result, initialize to 0 - "veor q4, q4, q4 \n"// result, initialize to 0 - - "1: \n" - "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples - "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples - "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation - "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs - "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation - - "vrev64.16 q2, q2 \n"// (reversed) 2 frames of the positive side - - "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs - "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets - "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits - "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits - - "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs - "vqrdmulh.s32 q11, q11, d2[1] \n"// interpolate (step3) 2nd set of coefs - "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits - "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits - - "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set - "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set - - "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef - "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by interpolated coef - - "vadd.s32 q0, q0, q12 \n"// accumulate result - "vadd.s32 q4, q4, q13 \n"// accumulate result - "vadd.s32 q0, q0, q14 \n"// accumulate result - "vadd.s32 q4, q4, q15 \n"// accumulate result - - "subs %[count], %[count], #4 \n"// update loop counter - "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples - - "bne 1b \n"// loop - - ASSEMBLY_ACCUMULATE_STEREO - - : [out] "=Uv" (out[0]), - [count] "+r" (count), - [coefsP0] "+r" (coefsP), - [coefsP1] "+r" (coefsP1), - [coefsN0] "+r" (coefsN), - [coefsN1] "+r" (coefsN1), - [sP] "+r" (sP), - [sN] "+r" (sN) - : [lerpP] "r" (lerpP), - [vLR] "r" (volumeLR) - : "cc", "memory", - "q0", "q1", "q2", "q3", "q4", - "q8", "q9", "q10", "q11", - "q12", "q13", "q14", "q15" - ); + ProcessNeonIntrinsic<2, 16, false>(out, count, coefsP, coefsN, sP, sN, volumeLR, + lerpP, coefsP1, coefsN1); } #endif //USE_NEON -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H*/ diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp index e6fb76c..ba9a356 100644 --- a/services/audioflinger/AudioResamplerSinc.cpp +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -61,135 +61,7 @@ namespace android { * cmd-line: fir -l 7 -s 48000 -c 20478 */ const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = { - 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, - 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592, - 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e, - 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f, - 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10, - 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e, - 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6, - 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5, - 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd, - 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb, - 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2, - 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3, - 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371, - 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921, - 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5, - 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564, - 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04, - 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab, - 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62, - 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230, - 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e, - 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337, - 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85, - 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112, - 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec, - 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d, - 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3, - 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb, - 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244, - 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c, - 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012, - 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075, - 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97, - 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486, - 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854, - 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812, - 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3, - 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7, - 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1, - 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5, - 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54, - 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533, - 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84, - 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c, - 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace, - 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef, - 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2, - 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d, - 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434, - 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc, - 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99, - 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781, - 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8, - 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125, - 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b, - 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070, - 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69, - 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b, - 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b, - 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e, - 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba, - 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3, - 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd, - 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f, - 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb, - 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7, - 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6, - 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae, - 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1, - 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5, - 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b, - 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8, - 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff, - 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422, - 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245, - 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79, - 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2, - 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760, - 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235, - 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64, - 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc, - 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f, - 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac, - 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5, - 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9, - 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67, - 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce, - 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e, - 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535, - 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50, - 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e, - 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c, - 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8, - 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d, - 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108, - 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5, - 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130, - 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4, - 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb, - 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21, - 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff, - 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f, - 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b, - 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb, - 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8, - 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa, - 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a, - 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e, - 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d, - 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0, - 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b, - 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65, - 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74, - 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e, - 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426, - 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2, - 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65, - 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5, - 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54, - 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7, - 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e, - 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f, - 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa, - 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2, - 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39, - 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f, - 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7, - 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10, - 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c, +#include "AudioResamplerSincUp.h" }; /* @@ -197,135 +69,7 @@ const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) * cmd-line: fir -l 7 -s 48000 -c 17189 */ const uint32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = { - 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, - 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d, - 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545, - 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86, - 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639, - 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b, - 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4, - 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce, - 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12, - 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9, - 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c, - 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1, - 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910, - 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f, - 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057, - 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b, - 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812, - 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02, - 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0, - 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf, - 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794, - 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934, - 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3, - 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2, - 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7, - 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542, - 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968, - 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b, - 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c, - 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef, - 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3, - 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c, - 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea, - 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf, - 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb, - 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0, - 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef, - 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508, - 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b, - 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9, - 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93, - 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9, - 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b, - 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9, - 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3, - 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa, - 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd, - 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c, - 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119, - 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821, - 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156, - 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8, - 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07, - 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962, - 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab, - 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1, - 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5, - 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976, - 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6, - 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5, - 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764, - 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473, - 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3, - 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6, - 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b, - 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686, - 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36, - 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d, - 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd, - 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9, - 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0, - 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7, - 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f, - 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a, - 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c, - 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7, - 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e, - 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5, - 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de, - 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d, - 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57, - 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf, - 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89, - 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa, - 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17, - 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5, - 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8, - 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5, - 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4, - 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329, - 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a, - 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd, - 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a, - 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857, - 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a, - 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c, - 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52, - 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6, - 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60, - 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86, - 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62, - 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec, - 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e, - 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef, - 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b, - 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a, - 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5, - 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8, - 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d, - 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d, - 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525, - 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e, - 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84, - 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453, - 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86, - 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a, - 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09, - 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552, - 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef, - 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df, - 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e, - 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9, - 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e, - 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a, - 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc, - 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1, - 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787, - 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae, - 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713, +#include "AudioResamplerSincDown.h" }; // we use 15 bits to interpolate between these samples @@ -521,7 +265,8 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, if (mConstants == &veryHighQualityConstants && readResampleCoefficients) { mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate ); } else { - mFirCoefs = (const int32_t *) ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown); + mFirCoefs = (const int32_t *) + ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown); } // select the appropriate resampler @@ -856,4 +601,4 @@ void AudioResamplerSinc::interpolate( } } // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h index 4691d0a..6d8e85d 100644 --- a/services/audioflinger/AudioResamplerSinc.h +++ b/services/audioflinger/AudioResamplerSinc.h @@ -95,6 +95,6 @@ private: }; // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/ diff --git a/services/audioflinger/AudioResamplerSincDown.h b/services/audioflinger/AudioResamplerSincDown.h new file mode 100644 index 0000000..2d0fb86 --- /dev/null +++ b/services/audioflinger/AudioResamplerSincDown.h @@ -0,0 +1,131 @@ +// cmd-line: fir -l 7 -s48000 -c 17189 + + 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, + 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d, + 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545, + 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86, + 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639, + 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b, + 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4, + 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce, + 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12, + 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9, + 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c, + 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1, + 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910, + 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f, + 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057, + 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b, + 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812, + 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02, + 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0, + 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf, + 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794, + 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934, + 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3, + 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2, + 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7, + 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542, + 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968, + 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b, + 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c, + 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef, + 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3, + 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c, + 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea, + 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf, + 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb, + 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0, + 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef, + 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508, + 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b, + 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9, + 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93, + 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9, + 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b, + 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9, + 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3, + 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa, + 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd, + 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c, + 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119, + 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821, + 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156, + 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8, + 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07, + 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962, + 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab, + 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1, + 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5, + 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976, + 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6, + 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5, + 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764, + 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473, + 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3, + 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6, + 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b, + 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686, + 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36, + 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d, + 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd, + 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9, + 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0, + 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7, + 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f, + 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a, + 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c, + 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7, + 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e, + 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5, + 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de, + 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d, + 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57, + 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf, + 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89, + 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa, + 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17, + 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5, + 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8, + 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5, + 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4, + 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329, + 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a, + 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd, + 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a, + 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857, + 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a, + 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c, + 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52, + 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6, + 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60, + 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86, + 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62, + 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec, + 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e, + 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef, + 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b, + 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a, + 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5, + 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8, + 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d, + 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d, + 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525, + 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e, + 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84, + 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453, + 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86, + 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a, + 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09, + 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552, + 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef, + 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df, + 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e, + 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9, + 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e, + 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a, + 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc, + 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1, + 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787, + 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae, + 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713, diff --git a/services/audioflinger/AudioResamplerSincUp.h b/services/audioflinger/AudioResamplerSincUp.h new file mode 100644 index 0000000..fd5367e --- /dev/null +++ b/services/audioflinger/AudioResamplerSincUp.h @@ -0,0 +1,131 @@ +// cmd-line: fir -l 7 -s48000 -c 20478 + + 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, + 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592, + 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e, + 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f, + 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10, + 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e, + 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6, + 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5, + 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd, + 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb, + 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2, + 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3, + 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371, + 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921, + 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5, + 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564, + 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04, + 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab, + 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62, + 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230, + 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e, + 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337, + 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85, + 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112, + 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec, + 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d, + 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3, + 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb, + 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244, + 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c, + 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012, + 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075, + 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97, + 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486, + 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854, + 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812, + 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3, + 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7, + 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1, + 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5, + 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54, + 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533, + 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84, + 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c, + 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace, + 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef, + 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2, + 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d, + 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434, + 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc, + 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99, + 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781, + 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8, + 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125, + 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b, + 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070, + 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69, + 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b, + 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b, + 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e, + 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba, + 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3, + 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd, + 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f, + 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb, + 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7, + 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6, + 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae, + 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1, + 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5, + 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b, + 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8, + 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff, + 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422, + 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245, + 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79, + 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2, + 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760, + 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235, + 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64, + 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc, + 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f, + 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac, + 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5, + 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9, + 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67, + 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce, + 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e, + 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535, + 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50, + 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e, + 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c, + 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8, + 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d, + 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108, + 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5, + 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130, + 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4, + 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb, + 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21, + 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff, + 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f, + 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b, + 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb, + 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8, + 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa, + 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a, + 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e, + 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d, + 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0, + 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b, + 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65, + 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74, + 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e, + 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426, + 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2, + 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65, + 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5, + 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54, + 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7, + 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e, + 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f, + 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa, + 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2, + 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39, + 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f, + 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7, + 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10, + 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c, diff --git a/services/audioflinger/AudioStreamOut.cpp b/services/audioflinger/AudioStreamOut.cpp new file mode 100644 index 0000000..e6d8f09 --- /dev/null +++ b/services/audioflinger/AudioStreamOut.cpp @@ -0,0 +1,117 @@ +/* +** +** Copyright 2015, 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 "AudioFlinger" +//#define LOG_NDEBUG 0 + +#include <hardware/audio.h> +#include <utils/Log.h> + +#include "AudioHwDevice.h" +#include "AudioStreamOut.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioStreamOut::AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags) + : audioHwDev(dev) + , stream(NULL) + , flags(flags) +{ +} + +audio_hw_device_t* AudioStreamOut::hwDev() const +{ + return audioHwDev->hwDevice(); +} + +status_t AudioStreamOut::getRenderPosition(uint32_t *frames) +{ + if (stream == NULL) { + return NO_INIT; + } + return stream->get_render_position(stream, frames); +} + +status_t AudioStreamOut::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) +{ + if (stream == NULL) { + return NO_INIT; + } + return stream->get_presentation_position(stream, frames, timestamp); +} + +status_t AudioStreamOut::open( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + const char *address) +{ + audio_stream_out_t* outStream; + int status = hwDev()->open_output_stream( + hwDev(), + handle, + devices, + flags, + config, + &outStream, + address); + ALOGV("AudioStreamOut::open(), HAL open_output_stream returned " + " %p, sampleRate %d, Format %#x, " + "channelMask %#x, status %d", + outStream, + config->sample_rate, + config->format, + config->channel_mask, + status); + + if (status == NO_ERROR) { + stream = outStream; + } + + return status; +} + +size_t AudioStreamOut::getFrameSize() +{ + ALOG_ASSERT(stream != NULL); + return audio_stream_out_frame_size(stream); +} + +int AudioStreamOut::flush() +{ + ALOG_ASSERT(stream != NULL); + if (stream->flush != NULL) { + return stream->flush(stream); + } + return NO_ERROR; +} + +int AudioStreamOut::standby() +{ + ALOG_ASSERT(stream != NULL); + return stream->common.standby(&stream->common); +} + +ssize_t AudioStreamOut::write(const void* buffer, size_t bytes) +{ + ALOG_ASSERT(stream != NULL); + return stream->write(stream, buffer, bytes); +} + +} // namespace android diff --git a/services/audioflinger/AudioStreamOut.h b/services/audioflinger/AudioStreamOut.h new file mode 100644 index 0000000..e91ca9c --- /dev/null +++ b/services/audioflinger/AudioStreamOut.h @@ -0,0 +1,83 @@ +/* +** +** Copyright 2015, 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. +*/ + +#ifndef ANDROID_AUDIO_STREAM_OUT_H +#define ANDROID_AUDIO_STREAM_OUT_H + +#include <stdint.h> +#include <sys/types.h> + +#include <system/audio.h> + +#include "AudioStreamOut.h" + +namespace android { + +class AudioHwDevice; + +/** + * Managed access to a HAL output stream. + */ +class AudioStreamOut { +public: +// AudioStreamOut is immutable, so its fields are const. +// For emphasis, we could also make all pointers to them be "const *", +// but that would clutter the code unnecessarily. + AudioHwDevice * const audioHwDev; + audio_stream_out_t *stream; + const audio_output_flags_t flags; + + audio_hw_device_t *hwDev() const; + + AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags); + + virtual status_t open( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + const char *address); + + virtual ~AudioStreamOut() { } + + virtual status_t getRenderPosition(uint32_t *frames); + + virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); + + /** + * Write audio buffer to driver. Returns number of bytes written, or a + * negative status_t. If at least one frame was written successfully prior to the error, + * it is suggested that the driver return that successful (short) byte count + * and then return an error in the subsequent call. + * + * If set_callback() has previously been called to enable non-blocking mode + * the write() is not allowed to block. It must write only the number of + * bytes that currently fit in the driver/hardware buffer and then return + * this byte count. If this is less than the requested write size the + * callback function must be called when more space is available in the + * driver/hardware buffer. + */ + virtual ssize_t write(const void *buffer, size_t bytes); + + virtual size_t getFrameSize(); + + virtual status_t flush(); + virtual status_t standby(); +}; + +} // namespace android + +#endif // ANDROID_AUDIO_STREAM_OUT_H diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h index 6a8aeb1..845697a 100644 --- a/services/audioflinger/Configuration.h +++ b/services/audioflinger/Configuration.h @@ -29,9 +29,8 @@ // uncomment to display CPU load adjusted for CPU frequency //#define CPU_FREQUENCY_STATISTICS -// uncomment to enable fast mixer to take performance samples for later statistical analysis -#define FAST_MIXER_STATISTICS -// FIXME rename to FAST_THREAD_STATISTICS +// uncomment to enable fast threads to take performance samples for later statistical analysis +#define FAST_THREAD_STATISTICS // uncomment for debugging timing problems related to StateQueue::push() //#define STATE_QUEUE_DUMP diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index bcaf8ae..8bccb47 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -1953,4 +1953,4 @@ void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread) } } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp index 0c9b976..9e7e8a4 100644 --- a/services/audioflinger/FastCapture.cpp +++ b/services/audioflinger/FastCapture.cpp @@ -29,18 +29,18 @@ namespace android { -/*static*/ const FastCaptureState FastCapture::initial; +/*static*/ const FastCaptureState FastCapture::sInitial; FastCapture::FastCapture() : FastThread(), - inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0), - readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0), - // dummyDumpState - totalNativeFramesRead(0) + mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), + mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0), + // mDummyDumpState + mTotalNativeFramesRead(0) { - previous = &initial; - current = &initial; + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastCaptureDumpState; } FastCapture::~FastCapture() @@ -63,13 +63,13 @@ void FastCapture::setLog(NBLog::Writer *logWriter __unused) void FastCapture::onIdle() { - preIdle = *(const FastCaptureState *)current; - current = &preIdle; + mPreIdle = *(const FastCaptureState *)mCurrent; + mCurrent = &mPreIdle; } void FastCapture::onExit() { - delete[] readBuffer; + free(mReadBuffer); } bool FastCapture::isSubClassCommand(FastThreadState::Command command) @@ -86,67 +86,67 @@ bool FastCapture::isSubClassCommand(FastThreadState::Command command) void FastCapture::onStateChange() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - const FastCaptureState * const previous = (const FastCaptureState *) this->previous; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + const FastCaptureState * const previous = (const FastCaptureState *) mPrevious; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; bool eitherChanged = false; // check for change in input HAL configuration - NBAIO_Format previousFormat = format; - if (current->mInputSourceGen != inputSourceGen) { - inputSource = current->mInputSource; - inputSourceGen = current->mInputSourceGen; - if (inputSource == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mInputSourceGen != mInputSourceGen) { + mInputSource = current->mInputSource; + mInputSourceGen = current->mInputSourceGen; + if (mInputSource == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; } else { - format = inputSource->format(); - sampleRate = Format_sampleRate(format); - unsigned channelCount = Format_channelCount(format); + mFormat = mInputSource->format(); + mSampleRate = Format_sampleRate(mFormat); + unsigned channelCount = Format_channelCount(mFormat); ALOG_ASSERT(channelCount == 1 || channelCount == 2); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; eitherChanged = true; } // check for change in pipe - if (current->mPipeSinkGen != pipeSinkGen) { - pipeSink = current->mPipeSink; - pipeSinkGen = current->mPipeSinkGen; + if (current->mPipeSinkGen != mPipeSinkGen) { + mPipeSink = current->mPipeSink; + mPipeSinkGen = current->mPipeSinkGen; eitherChanged = true; } // input source and pipe sink must be compatible - if (eitherChanged && inputSource != NULL && pipeSink != NULL) { - ALOG_ASSERT(Format_isEqual(format, pipeSink->format())); + if (eitherChanged && mInputSource != NULL && mPipeSink != NULL) { + ALOG_ASSERT(Format_isEqual(mFormat, mPipeSink->format())); } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { - // FIXME to avoid priority inversion, don't delete here - delete[] readBuffer; - readBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { + // FIXME to avoid priority inversion, don't free here + free(mReadBuffer); + mReadBuffer = NULL; + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal capture thread allocate for // us to avoid blocking here and to prevent possible priority inversion - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - readBuffer = new short[frameCount * channelCount]; - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + (void)posix_memalign(&mReadBuffer, 32, frameCount * Format_frameSize(mFormat)); + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNs = 0; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } - readBufferState = -1; + mReadBufferState = -1; dumpState->mFrameCount = frameCount; } @@ -154,44 +154,43 @@ void FastCapture::onStateChange() void FastCapture::onWork() { - const FastCaptureState * const current = (const FastCaptureState *) this->current; - FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState; - const FastCaptureState::Command command = this->command; + const FastCaptureState * const current = (const FastCaptureState *) mCurrent; + FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) mDumpState; + const FastCaptureState::Command command = mCommand; const size_t frameCount = current->mFrameCount; if ((command & FastCaptureState::READ) /*&& isWarm*/) { - ALOG_ASSERT(inputSource != NULL); - ALOG_ASSERT(readBuffer != NULL); + ALOG_ASSERT(mInputSource != NULL); + ALOG_ASSERT(mReadBuffer != NULL); dumpState->mReadSequence++; ATRACE_BEGIN("read"); - ssize_t framesRead = inputSource->read(readBuffer, frameCount, + ssize_t framesRead = mInputSource->read(mReadBuffer, frameCount, AudioBufferProvider::kInvalidPTS); ATRACE_END(); dumpState->mReadSequence++; if (framesRead >= 0) { LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount); - totalNativeFramesRead += framesRead; - dumpState->mFramesRead = totalNativeFramesRead; - readBufferState = framesRead; + mTotalNativeFramesRead += framesRead; + dumpState->mFramesRead = mTotalNativeFramesRead; + mReadBufferState = framesRead; } else { dumpState->mReadErrors++; - readBufferState = 0; + mReadBufferState = 0; } // FIXME rename to attemptedIO - attemptedWrite = true; + mAttemptedWrite = true; } if (command & FastCaptureState::WRITE) { - ALOG_ASSERT(pipeSink != NULL); - ALOG_ASSERT(readBuffer != NULL); - if (readBufferState < 0) { - unsigned channelCount = Format_channelCount(format); - // FIXME frameSize - memset(readBuffer, 0, frameCount * channelCount * sizeof(short)); - readBufferState = frameCount; + ALOG_ASSERT(mPipeSink != NULL); + ALOG_ASSERT(mReadBuffer != NULL); + if (mReadBufferState < 0) { + unsigned channelCount = Format_channelCount(mFormat); + memset(mReadBuffer, 0, frameCount * Format_frameSize(mFormat)); + mReadBufferState = frameCount; } - if (readBufferState > 0) { - ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState); + if (mReadBufferState > 0) { + ssize_t framesWritten = mPipeSink->write(mReadBuffer, mReadBufferState); // FIXME This supports at most one fast capture client. // To handle multiple clients this could be converted to an array, // or with a lot more work the control block could be shared by all clients. @@ -210,13 +209,4 @@ void FastCapture::onWork() } } -FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), - mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) -{ -} - -FastCaptureDumpState::~FastCaptureDumpState() -{ -} - } // namespace android diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h index e535b9d..e258a4d 100644 --- a/services/audioflinger/FastCapture.h +++ b/services/audioflinger/FastCapture.h @@ -20,23 +20,12 @@ #include "FastThread.h" #include "StateQueue.h" #include "FastCaptureState.h" +#include "FastCaptureDumpState.h" namespace android { typedef StateQueue<FastCaptureState> FastCaptureStateQueue; -struct FastCaptureDumpState : FastThreadDumpState { - FastCaptureDumpState(); - /*virtual*/ ~FastCaptureDumpState(); - - // FIXME by renaming, could pull up many of these to FastThreadDumpState - uint32_t mReadSequence; // incremented before and after each read() - uint32_t mFramesRead; // total number of frames read successfully - uint32_t mReadErrors; // total number of read() errors - uint32_t mSampleRate; - size_t mFrameCount; -}; - class FastCapture : public FastThread { public: @@ -57,19 +46,21 @@ private: virtual void onStateChange(); virtual void onWork(); - static const FastCaptureState initial; - FastCaptureState preIdle; // copy of state before we went into idle + static const FastCaptureState sInitial; + + FastCaptureState mPreIdle; // copy of state before we went into idle // FIXME by renaming, could pull up many of these to FastThread - NBAIO_Source *inputSource; - int inputSourceGen; - NBAIO_Sink *pipeSink; - int pipeSinkGen; - short *readBuffer; - ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear - NBAIO_Format format; - unsigned sampleRate; - FastCaptureDumpState dummyDumpState; - uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead + NBAIO_Source* mInputSource; + int mInputSourceGen; + NBAIO_Sink* mPipeSink; + int mPipeSinkGen; + void* mReadBuffer; + ssize_t mReadBufferState; // number of initialized frames in readBuffer, + // or -1 to clear + NBAIO_Format mFormat; + unsigned mSampleRate; + FastCaptureDumpState mDummyFastCaptureDumpState; + uint32_t mTotalNativeFramesRead; // copied to dumpState->mFramesRead }; // class FastCapture diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp new file mode 100644 index 0000000..53eeba5 --- /dev/null +++ b/services/audioflinger/FastCaptureDumpState.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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 "FastCaptureDumpState" +//define LOG_NDEBUG 0 + +#include "Configuration.h" +#include <utils/Log.h> +#include "FastCaptureDumpState.h" +#include "FastCaptureState.h" + +namespace android { + +FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(), + mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0) +{ +} + +FastCaptureDumpState::~FastCaptureDumpState() +{ +} + +void FastCaptureDumpState::dump(int fd) const +{ + if (mCommand == FastCaptureState::INITIAL) { + dprintf(fd, " FastCapture not initialized\n"); + return; + } + double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + + (mMeasuredWarmupTs.tv_nsec / 1000000.0); + double periodSec = (double) mFrameCount / mSampleRate; + dprintf(fd, " FastCapture command=%s readSequence=%u framesRead=%u\n" + " readErrors=%u sampleRate=%u frameCount=%zu\n" + " measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n", + FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead, + mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, + periodSec * 1e3); +} + +} // android diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h new file mode 100644 index 0000000..6f9c4c3 --- /dev/null +++ b/services/audioflinger/FastCaptureDumpState.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_AUDIO_FAST_CAPTURE_DUMP_STATE_H +#define ANDROID_AUDIO_FAST_CAPTURE_DUMP_STATE_H + +#include <stdint.h> +#include "Configuration.h" +#include "FastThreadDumpState.h" + +namespace android { + +struct FastCaptureDumpState : FastThreadDumpState { + FastCaptureDumpState(); + /*virtual*/ ~FastCaptureDumpState(); + + void dump(int fd) const; // should only be called on a stable copy, not the original + + // FIXME by renaming, could pull up many of these to FastThreadDumpState + uint32_t mReadSequence; // incremented before and after each read() + uint32_t mFramesRead; // total number of frames read successfully + uint32_t mReadErrors; // total number of read() errors + uint32_t mSampleRate; + size_t mFrameCount; +}; + +} // android + +#endif // ANDROID_AUDIO_FAST_CAPTURE_DUMP_STATE_H diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp index 1d029b7..c4d5e45 100644 --- a/services/audioflinger/FastCaptureState.cpp +++ b/services/audioflinger/FastCaptureState.cpp @@ -27,4 +27,19 @@ FastCaptureState::~FastCaptureState() { } +// static +const char *FastCaptureState::commandToString(Command command) +{ + const char *str = FastThreadState::commandToString(command); + if (str != NULL) { + return str; + } + switch (command) { + case FastCaptureState::READ: return "READ"; + case FastCaptureState::WRITE: return "WRITE"; + case FastCaptureState::READ_WRITE: return "READ_WRITE"; + } + LOG_ALWAYS_FATAL("%s", __func__); +} + } // android diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h index 29c865a..9bca2d4 100644 --- a/services/audioflinger/FastCaptureState.h +++ b/services/audioflinger/FastCaptureState.h @@ -29,21 +29,23 @@ struct FastCaptureState : FastThreadState { /*virtual*/ ~FastCaptureState(); // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread - NBAIO_Source *mInputSource; // HAL input device, must already be negotiated + NBAIO_Source* mInputSource; // HAL input device, must already be negotiated // FIXME by renaming, could pull up these fields to FastThreadState int mInputSourceGen; // increment when mInputSource is assigned - NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink + NBAIO_Sink* mPipeSink; // after reading from input source, write to this pipe sink int mPipeSinkGen; // increment when mPipeSink is assigned size_t mFrameCount; // number of frames per fast capture buffer - audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL + audio_track_cblk_t* mCblk; // control block for the single fast client, or NULL // Extends FastThreadState::Command static const Command // The following commands also process configuration changes, and can be "or"ed: - READ = 0x8, // read from input source - WRITE = 0x10, // write to pipe sink - READ_WRITE = 0x18; // read from input source and write to pipe sink + READ = 0x8, // read from input source + WRITE = 0x10, // write to pipe sink + READ_WRITE = 0x18; // read from input source and write to pipe sink + // never returns NULL; asserts if command is invalid + static const char *commandToString(Command command); }; // struct FastCaptureState } // namespace android diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 2678cbf..f1cf0aa 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -27,10 +27,11 @@ #include "Configuration.h" #include <time.h> +#include <utils/Debug.h> #include <utils/Log.h> #include <utils/Trace.h> #include <system/audio.h> -#ifdef FAST_MIXER_STATISTICS +#ifdef FAST_THREAD_STATISTICS #include <cpustats/CentralTendencyStatistics.h> #ifdef CPU_FREQUENCY_STATISTICS #include <cpustats/ThreadCpuUsage.h> @@ -44,15 +45,15 @@ namespace android { -/*static*/ const FastMixerState FastMixer::initial; +/*static*/ const FastMixerState FastMixer::sInitial; FastMixer::FastMixer() : FastThread(), - slopNs(0), - // fastTrackNames - // generations - outputSink(NULL), - outputSinkGen(0), - mixer(NULL), + mSlopNs(0), + // mFastTrackNames + // mGenerations + mOutputSink(NULL), + mOutputSinkGen(0), + mMixer(NULL), mSinkBuffer(NULL), mSinkBufferSize(0), mSinkChannelCount(FCC_2), @@ -60,30 +61,30 @@ FastMixer::FastMixer() : FastThread(), mMixerBufferSize(0), mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), mMixerBufferState(UNDEFINED), - format(Format_Invalid), - sampleRate(0), - fastTracksGen(0), - totalNativeFramesWritten(0), + mFormat(Format_Invalid), + mSampleRate(0), + mFastTracksGen(0), + mTotalNativeFramesWritten(0), // timestamp - nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler + mNativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler { - // FIXME pass initial as parameter to base class constructor, and make it static local - previous = &initial; - current = &initial; + // FIXME pass sInitial as parameter to base class constructor, and make it static local + mPrevious = &sInitial; + mCurrent = &sInitial; - mDummyDumpState = &dummyDumpState; + mDummyDumpState = &mDummyFastMixerDumpState; // TODO: Add channel mask to NBAIO_Format. // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); unsigned i; for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; - generations[i] = 0; + mFastTrackNames[i] = -1; + mGenerations[i] = 0; } -#ifdef FAST_MIXER_STATISTICS - oldLoad.tv_sec = 0; - oldLoad.tv_nsec = 0; +#ifdef FAST_THREAD_STATISTICS + mOldLoad.tv_sec = 0; + mOldLoad.tv_nsec = 0; #endif } @@ -103,20 +104,20 @@ const FastThreadState *FastMixer::poll() void FastMixer::setLog(NBLog::Writer *logWriter) { - if (mixer != NULL) { - mixer->setLog(logWriter); + if (mMixer != NULL) { + mMixer->setLog(logWriter); } } void FastMixer::onIdle() { - preIdle = *(const FastMixerState *)current; - current = &preIdle; + mPreIdle = *(const FastMixerState *)mCurrent; + mCurrent = &mPreIdle; } void FastMixer::onExit() { - delete mixer; + delete mMixer; free(mMixerBuffer); free(mSinkBuffer); } @@ -135,82 +136,84 @@ bool FastMixer::isSubClassCommand(FastThreadState::Command command) void FastMixer::onStateChange() { - const FastMixerState * const current = (const FastMixerState *) this->current; - const FastMixerState * const previous = (const FastMixerState *) this->previous; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + const FastMixerState * const previous = (const FastMixerState *) mPrevious; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; const size_t frameCount = current->mFrameCount; // handle state change here, but since we want to diff the state, - // we're prepared for previous == &initial the first time through + // we're prepared for previous == &sInitial the first time through unsigned previousTrackMask; // check for change in output HAL configuration - NBAIO_Format previousFormat = format; - if (current->mOutputSinkGen != outputSinkGen) { - outputSink = current->mOutputSink; - outputSinkGen = current->mOutputSinkGen; - if (outputSink == NULL) { - format = Format_Invalid; - sampleRate = 0; + NBAIO_Format previousFormat = mFormat; + if (current->mOutputSinkGen != mOutputSinkGen) { + mOutputSink = current->mOutputSink; + mOutputSinkGen = current->mOutputSinkGen; + if (mOutputSink == NULL) { + mFormat = Format_Invalid; + mSampleRate = 0; mSinkChannelCount = 0; mSinkChannelMask = AUDIO_CHANNEL_NONE; } else { - format = outputSink->format(); - sampleRate = Format_sampleRate(format); - mSinkChannelCount = Format_channelCount(format); + mFormat = mOutputSink->format(); + mSampleRate = Format_sampleRate(mFormat); + mSinkChannelCount = Format_channelCount(mFormat); LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); // TODO: Add channel mask to NBAIO_Format // We assume that the channel mask must be a valid positional channel mask. mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); } - dumpState->mSampleRate = sampleRate; + dumpState->mSampleRate = mSampleRate; } - if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { + if ((!Format_isEqual(mFormat, previousFormat)) || (frameCount != previous->mFrameCount)) { // FIXME to avoid priority inversion, don't delete here - delete mixer; - mixer = NULL; + delete mMixer; + mMixer = NULL; free(mMixerBuffer); mMixerBuffer = NULL; free(mSinkBuffer); mSinkBuffer = NULL; - if (frameCount > 0 && sampleRate > 0) { + if (frameCount > 0 && mSampleRate > 0) { // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion - mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); + mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::kMaxFastTracks); const size_t mixerFrameSize = mSinkChannelCount * audio_bytes_per_sample(mMixerBufferFormat); mMixerBufferSize = mixerFrameSize * frameCount; (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); const size_t sinkFrameSize = mSinkChannelCount - * audio_bytes_per_sample(format.mFormat); + * audio_bytes_per_sample(mFormat.mFormat); if (sinkFrameSize > mixerFrameSize) { // need a sink buffer mSinkBufferSize = sinkFrameSize * frameCount; (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); } - periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 - underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 - forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 - warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + mPeriodNs = (frameCount * 1000000000LL) / mSampleRate; // 1.00 + mUnderrunNs = (frameCount * 1750000000LL) / mSampleRate; // 1.75 + mOverrunNs = (frameCount * 500000000LL) / mSampleRate; // 0.50 + mForceNs = (frameCount * 950000000LL) / mSampleRate; // 0.95 + mWarmupNsMin = (frameCount * 750000000LL) / mSampleRate; // 0.75 + mWarmupNsMax = (frameCount * 1250000000LL) / mSampleRate; // 1.25 } else { - periodNs = 0; - underrunNs = 0; - overrunNs = 0; - forceNs = 0; - warmupNs = 0; + mPeriodNs = 0; + mUnderrunNs = 0; + mOverrunNs = 0; + mForceNs = 0; + mWarmupNsMin = 0; + mWarmupNsMax = LONG_MAX; } mMixerBufferState = UNDEFINED; #if !LOG_NDEBUG for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; } #endif // we need to reconfigure all active tracks previousTrackMask = 0; - fastTracksGen = current->mFastTracksGen - 1; + mFastTracksGen = current->mFastTracksGen - 1; dumpState->mFrameCount = frameCount; } else { previousTrackMask = previous->mTrackMask; @@ -219,7 +222,7 @@ void FastMixer::onStateChange() // check for change in active track set const unsigned currentTrackMask = current->mTrackMask; dumpState->mTrackMask = currentTrackMask; - if (current->mFastTracksGen != fastTracksGen) { + if (current->mFastTracksGen != mFastTracksGen) { ALOG_ASSERT(mMixerBuffer != NULL); int name; @@ -230,16 +233,16 @@ void FastMixer::onStateChange() removedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; ALOG_ASSERT(fastTrack->mBufferProvider == NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->deleteTrackName(name); + mMixer->deleteTrackName(name); } #if !LOG_NDEBUG - fastTrackNames[i] = -1; + mFastTrackNames[i] = -1; #endif // don't reset track dump state, since other side is ignoring it - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // now process added tracks @@ -249,29 +252,29 @@ void FastMixer::onStateChange() addedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); - if (mixer != NULL) { - name = mixer->getTrackName(fastTrack->mChannelMask, + ALOG_ASSERT(bufferProvider != NULL && mFastTrackNames[i] == -1); + if (mMixer != NULL) { + name = mMixer->getTrackName(fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); - fastTrackNames[i] = name; - mixer->setBufferProvider(name, bufferProvider); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + mFastTrackNames[i] = name; + mMixer->setBufferProvider(name, bufferProvider); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer); // newly allocated track names default to full scale volume - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); - mixer->enable(name); + mMixer->enable(name); } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } // finally process (potentially) modified tracks; these use the same slot @@ -281,38 +284,38 @@ void FastMixer::onStateChange() int i = __builtin_ctz(modifiedTracks); modifiedTracks &= ~(1 << i); const FastTrack* fastTrack = ¤t->mFastTracks[i]; - if (fastTrack->mGeneration != generations[i]) { + if (fastTrack->mGeneration != mGenerations[i]) { // this track was actually modified AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; ALOG_ASSERT(bufferProvider != NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; + if (mMixer != NULL) { + name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); - mixer->setBufferProvider(name, bufferProvider); + mMixer->setBufferProvider(name, bufferProvider); if (fastTrack->mVolumeProvider == NULL) { float f = AudioMixer::UNITY_GAIN_FLOAT; - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); } - mixer->setParameter(name, AudioMixer::RESAMPLE, + mMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::REMOVE, NULL); - mixer->setParameter( + mMixer->setParameter( name, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, (void *)(uintptr_t)fastTrack->mFormat); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); // already enabled } - generations[i] = fastTrack->mGeneration; + mGenerations[i] = fastTrack->mGeneration; } } - fastTracksGen = current->mFastTracksGen; + mFastTracksGen = current->mFastTracksGen; dumpState->mNumTracks = popcount(currentTrackMask); } @@ -320,12 +323,12 @@ void FastMixer::onStateChange() void FastMixer::onWork() { - const FastMixerState * const current = (const FastMixerState *) this->current; - FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; - const FastMixerState::Command command = this->command; + const FastMixerState * const current = (const FastMixerState *) mCurrent; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) mDumpState; + const FastMixerState::Command command = mCommand; const size_t frameCount = current->mFrameCount; - if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { + if ((command & FastMixerState::MIX) && (mMixer != NULL) && mIsWarm) { ALOG_ASSERT(mMixerBuffer != NULL); // for each track, update volume and check for underrun unsigned currentTrackMask = current->mTrackMask; @@ -335,9 +338,9 @@ void FastMixer::onWork() const FastTrack* fastTrack = ¤t->mFastTracks[i]; // Refresh the per-track timestamp - if (timestampStatus == NO_ERROR) { + if (mTimestampStatus == NO_ERROR) { uint32_t trackFramesWrittenButNotPresented = - nativeFramesWrittenButNotPresented; + mNativeFramesWrittenButNotPresented; uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); // Can't provide an AudioTimestamp before first frame presented, // or during the brief 32-bit wraparound window @@ -345,20 +348,20 @@ void FastMixer::onWork() AudioTimestamp perTrackTimestamp; perTrackTimestamp.mPosition = trackFramesWritten - trackFramesWrittenButNotPresented; - perTrackTimestamp.mTime = timestamp.mTime; + perTrackTimestamp.mTime = mTimestamp.mTime; fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); } } - int name = fastTrackNames[i]; + int name = mFastTrackNames[i]; ALOG_ASSERT(name >= 0); if (fastTrack->mVolumeProvider != NULL) { gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); } // FIXME The current implementation of framesReady() for fast tracks // takes a tryLock, which can block @@ -379,43 +382,44 @@ void FastMixer::onWork() if (framesReady == 0) { underruns.mBitFields.mEmpty++; underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; - mixer->disable(name); + mMixer->disable(name); } else { // allow mixing partial buffer underruns.mBitFields.mPartial++; underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; - mixer->enable(name); + mMixer->enable(name); } } else { underruns.mBitFields.mFull++; underruns.mBitFields.mMostRecent = UNDERRUN_FULL; - mixer->enable(name); + mMixer->enable(name); } ftDump->mUnderruns = underruns; ftDump->mFramesReady = framesReady; } int64_t pts; - if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { + if (mOutputSink == NULL || (OK != mOutputSink->getNextWriteTimestamp(&pts))) { pts = AudioBufferProvider::kInvalidPTS; } // process() is CPU-bound - mixer->process(pts); + mMixer->process(pts); mMixerBufferState = MIXED; } else if (mMixerBufferState == MIXED) { mMixerBufferState = UNDEFINED; } //bool didFullWrite = false; // dumpsys could display a count of partial writes - if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { + if ((command & FastMixerState::WRITE) && (mOutputSink != NULL) && (mMixerBuffer != NULL)) { if (mMixerBufferState == UNDEFINED) { memset(mMixerBuffer, 0, mMixerBufferSize); mMixerBufferState = ZEROED; } + // prepare the buffer used to write to sink void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; - if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format - memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat, - frameCount * Format_channelCount(format)); + if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format + memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, + frameCount * Format_channelCount(mFormat)); } // if non-NULL, then duplicate write() to this non-blocking sink NBAIO_Sink* teeSink; @@ -426,252 +430,34 @@ void FastMixer::onWork() // but this code should be modified to handle both non-blocking and blocking sinks dumpState->mWriteSequence++; ATRACE_BEGIN("write"); - ssize_t framesWritten = outputSink->write(buffer, frameCount); + ssize_t framesWritten = mOutputSink->write(buffer, frameCount); ATRACE_END(); dumpState->mWriteSequence++; if (framesWritten >= 0) { ALOG_ASSERT((size_t) framesWritten <= frameCount); - totalNativeFramesWritten += framesWritten; - dumpState->mFramesWritten = totalNativeFramesWritten; + mTotalNativeFramesWritten += framesWritten; + dumpState->mFramesWritten = mTotalNativeFramesWritten; //if ((size_t) framesWritten == frameCount) { // didFullWrite = true; //} } else { dumpState->mWriteErrors++; } - attemptedWrite = true; + mAttemptedWrite = true; // FIXME count # of writes blocked excessively, CPU usage, etc. for dump - timestampStatus = outputSink->getTimestamp(timestamp); - if (timestampStatus == NO_ERROR) { - uint32_t totalNativeFramesPresented = timestamp.mPosition; - if (totalNativeFramesPresented <= totalNativeFramesWritten) { - nativeFramesWrittenButNotPresented = - totalNativeFramesWritten - totalNativeFramesPresented; + mTimestampStatus = mOutputSink->getTimestamp(mTimestamp); + if (mTimestampStatus == NO_ERROR) { + uint32_t totalNativeFramesPresented = mTimestamp.mPosition; + if (totalNativeFramesPresented <= mTotalNativeFramesWritten) { + mNativeFramesWrittenButNotPresented = + mTotalNativeFramesWritten - totalNativeFramesPresented; } else { // HAL reported that more frames were presented than were written - timestampStatus = INVALID_OPERATION; + mTimestampStatus = INVALID_OPERATION; } } } } -FastMixerDumpState::FastMixerDumpState( -#ifdef FAST_MIXER_STATISTICS - uint32_t samplingN -#endif - ) : FastThreadDumpState(), - mWriteSequence(0), mFramesWritten(0), - mNumTracks(0), mWriteErrors(0), - mSampleRate(0), mFrameCount(0), - mTrackMask(0) -{ -#ifdef FAST_MIXER_STATISTICS - increaseSamplingN(samplingN); -#endif -} - -#ifdef FAST_MIXER_STATISTICS -void FastMixerDumpState::increaseSamplingN(uint32_t samplingN) -{ - if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { - return; - } - uint32_t additional = samplingN - mSamplingN; - // sample arrays aren't accessed atomically with respect to the bounds, - // so clearing reduces chance for dumpsys to read random uninitialized samples - memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); - memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); -#ifdef CPU_FREQUENCY_STATISTICS - memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); -#endif - mSamplingN = samplingN; -} -#endif - -FastMixerDumpState::~FastMixerDumpState() -{ -} - -// helper function called by qsort() -static int compare_uint32_t(const void *pa, const void *pb) -{ - uint32_t a = *(const uint32_t *)pa; - uint32_t b = *(const uint32_t *)pb; - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } else { - return 0; - } -} - -void FastMixerDumpState::dump(int fd) const -{ - if (mCommand == FastMixerState::INITIAL) { - dprintf(fd, " FastMixer not initialized\n"); - return; - } -#define COMMAND_MAX 32 - char string[COMMAND_MAX]; - switch (mCommand) { - case FastMixerState::INITIAL: - strcpy(string, "INITIAL"); - break; - case FastMixerState::HOT_IDLE: - strcpy(string, "HOT_IDLE"); - break; - case FastMixerState::COLD_IDLE: - strcpy(string, "COLD_IDLE"); - break; - case FastMixerState::EXIT: - strcpy(string, "EXIT"); - break; - case FastMixerState::MIX: - strcpy(string, "MIX"); - break; - case FastMixerState::WRITE: - strcpy(string, "WRITE"); - break; - case FastMixerState::MIX_WRITE: - strcpy(string, "MIX_WRITE"); - break; - default: - snprintf(string, COMMAND_MAX, "%d", mCommand); - break; - } - double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + - (mMeasuredWarmupTs.tv_nsec / 1000000.0); - double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" - " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" - " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", - string, mWriteSequence, mFramesWritten, - mNumTracks, mWriteErrors, mUnderruns, mOverruns, - mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, - mixPeriodSec * 1e3); -#ifdef FAST_MIXER_STATISTICS - // find the interval of valid samples - uint32_t bounds = mBounds; - uint32_t newestOpen = bounds & 0xFFFF; - uint32_t oldestClosed = bounds >> 16; - uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; - if (n > mSamplingN) { - ALOGE("too many samples %u", n); - n = mSamplingN; - } - // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, - // and adjusted CPU load in MHz normalized for CPU clock frequency - CentralTendencyStatistics wall, loadNs; -#ifdef CPU_FREQUENCY_STATISTICS - CentralTendencyStatistics kHz, loadMHz; - uint32_t previousCpukHz = 0; -#endif - // Assuming a normal distribution for cycle times, three standard deviations on either side of - // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the - // sample set, we get 99.8% combined, or close to three standard deviations. - static const uint32_t kTailDenominator = 1000; - uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; - // loop over all the samples - for (uint32_t j = 0; j < n; ++j) { - size_t i = oldestClosed++ & (mSamplingN - 1); - uint32_t wallNs = mMonotonicNs[i]; - if (tail != NULL) { - tail[j] = wallNs; - } - wall.sample(wallNs); - uint32_t sampleLoadNs = mLoadNs[i]; - loadNs.sample(sampleLoadNs); -#ifdef CPU_FREQUENCY_STATISTICS - uint32_t sampleCpukHz = mCpukHz[i]; - // skip bad kHz samples - if ((sampleCpukHz & ~0xF) != 0) { - kHz.sample(sampleCpukHz >> 4); - if (sampleCpukHz == previousCpukHz) { - double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; - double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 - loadMHz.sample(adjMHz); - } - } - previousCpukHz = sampleCpukHz; -#endif - } - if (n) { - dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", - wall.n() * mixPeriodSec); - dprintf(fd, " wall clock time in ms per mix cycle:\n" - " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, - wall.stddev()*1e-6); - dprintf(fd, " raw CPU load in us per mix cycle:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); - } else { - dprintf(fd, " No FastMixer statistics available currently\n"); - } -#ifdef CPU_FREQUENCY_STATISTICS - dprintf(fd, " CPU clock frequency in MHz:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); - dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" - " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", - loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); -#endif - if (tail != NULL) { - qsort(tail, n, sizeof(uint32_t), compare_uint32_t); - // assume same number of tail samples on each side, left and right - uint32_t count = n / kTailDenominator; - CentralTendencyStatistics left, right; - for (uint32_t i = 0; i < count; ++i) { - left.sample(tail[i]); - right.sample(tail[n - (i + 1)]); - } - dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" - " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" - " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, - right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, - right.stddev()*1e-6); - delete[] tail; - } -#endif - // The active track mask and track states are updated non-atomically. - // So if we relied on isActive to decide whether to display, - // then we might display an obsolete track or omit an active track. - // Instead we always display all tracks, with an indication - // of whether we think the track is active. - uint32_t trackMask = mTrackMask; - dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", - FastMixerState::kMaxFastTracks, trackMask); - dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); - for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { - bool isActive = trackMask & 1; - const FastTrackDump *ftDump = &mTracks[i]; - const FastTrackUnderruns& underruns = ftDump->mUnderruns; - const char *mostRecent; - switch (underruns.mBitFields.mMostRecent) { - case UNDERRUN_FULL: - mostRecent = "full"; - break; - case UNDERRUN_PARTIAL: - mostRecent = "partial"; - break; - case UNDERRUN_EMPTY: - mostRecent = "empty"; - break; - default: - mostRecent = "?"; - break; - } - dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", - (underruns.mBitFields.mFull) & UNDERRUN_MASK, - (underruns.mBitFields.mPartial) & UNDERRUN_MASK, - (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, - mostRecent, ftDump->mFramesReady); - } -} - } // namespace android diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index fde8c2b..06a68fb 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -17,11 +17,7 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_H #define ANDROID_AUDIO_FAST_MIXER_H -#include <linux/futex.h> -#include <sys/syscall.h> -#include <utils/Debug.h> #include "FastThread.h" -#include <utils/Thread.h> #include "StateQueue.h" #include "FastMixerState.h" #include "FastMixerDumpState.h" @@ -52,36 +48,39 @@ private: virtual void onStateChange(); virtual void onWork(); - // FIXME these former local variables need comments and to be renamed to have "m" prefix - static const FastMixerState initial; - FastMixerState preIdle; // copy of state before we went into idle - long slopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) - int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks - int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration - NBAIO_Sink *outputSink; - int outputSinkGen; - AudioMixer* mixer; + // FIXME these former local variables need comments + static const FastMixerState sInitial; + + FastMixerState mPreIdle; // copy of state before we went into idle + long mSlopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) + int mFastTrackNames[FastMixerState::kMaxFastTracks]; + // handles used by mixer to identify tracks + int mGenerations[FastMixerState::kMaxFastTracks]; + // last observed mFastTracks[i].mGeneration + NBAIO_Sink* mOutputSink; + int mOutputSinkGen; + AudioMixer* mMixer; // mSinkBuffer audio format is stored in format.mFormat. - void* mSinkBuffer; // used for mixer output format translation + void* mSinkBuffer; // used for mixer output format translation // if sink format is different than mixer output. - size_t mSinkBufferSize; - uint32_t mSinkChannelCount; + size_t mSinkBufferSize; + uint32_t mSinkChannelCount; audio_channel_mask_t mSinkChannelMask; - void* mMixerBuffer; // mixer output buffer. - size_t mMixerBufferSize; - audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + void* mMixerBuffer; // mixer output buffer. + size_t mMixerBufferSize; + audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; - NBAIO_Format format; - unsigned sampleRate; - int fastTracksGen; - FastMixerDumpState dummyDumpState; - uint32_t totalNativeFramesWritten; // copied to dumpState->mFramesWritten + NBAIO_Format mFormat; + unsigned mSampleRate; + int mFastTracksGen; + FastMixerDumpState mDummyFastMixerDumpState; + uint32_t mTotalNativeFramesWritten; // copied to dumpState->mFramesWritten // next 2 fields are valid only when timestampStatus == NO_ERROR - AudioTimestamp timestamp; - uint32_t nativeFramesWrittenButNotPresented; + AudioTimestamp mTimestamp; + uint32_t mNativeFramesWrittenButNotPresented; }; // class FastMixer diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp new file mode 100644 index 0000000..b10942b --- /dev/null +++ b/services/audioflinger/FastMixerDumpState.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2014 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 "FastMixerDumpState" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#ifdef FAST_THREAD_STATISTICS +#include <cpustats/CentralTendencyStatistics.h> +#ifdef CPU_FREQUENCY_STATISTICS +#include <cpustats/ThreadCpuUsage.h> +#endif +#endif +#include <utils/Debug.h> +#include <utils/Log.h> +#include "FastMixerDumpState.h" + +namespace android { + +FastMixerDumpState::FastMixerDumpState() : FastThreadDumpState(), + mWriteSequence(0), mFramesWritten(0), + mNumTracks(0), mWriteErrors(0), + mSampleRate(0), mFrameCount(0), + mTrackMask(0) +{ +} + +FastMixerDumpState::~FastMixerDumpState() +{ +} + +// helper function called by qsort() +static int compare_uint32_t(const void *pa, const void *pb) +{ + uint32_t a = *(const uint32_t *)pa; + uint32_t b = *(const uint32_t *)pb; + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } +} + +void FastMixerDumpState::dump(int fd) const +{ + if (mCommand == FastMixerState::INITIAL) { + dprintf(fd, " FastMixer not initialized\n"); + return; + } + double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + + (mMeasuredWarmupTs.tv_nsec / 1000000.0); + double mixPeriodSec = (double) mFrameCount / mSampleRate; + dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" + " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" + " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" + " mixPeriod=%.2f ms\n", + FastMixerState::commandToString(mCommand), mWriteSequence, mFramesWritten, + mNumTracks, mWriteErrors, mUnderruns, mOverruns, + mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, + mixPeriodSec * 1e3); +#ifdef FAST_THREAD_STATISTICS + // find the interval of valid samples + uint32_t bounds = mBounds; + uint32_t newestOpen = bounds & 0xFFFF; + uint32_t oldestClosed = bounds >> 16; + uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; + if (n > mSamplingN) { + ALOGE("too many samples %u", n); + n = mSamplingN; + } + // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, + // and adjusted CPU load in MHz normalized for CPU clock frequency + CentralTendencyStatistics wall, loadNs; +#ifdef CPU_FREQUENCY_STATISTICS + CentralTendencyStatistics kHz, loadMHz; + uint32_t previousCpukHz = 0; +#endif + // Assuming a normal distribution for cycle times, three standard deviations on either side of + // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the + // sample set, we get 99.8% combined, or close to three standard deviations. + static const uint32_t kTailDenominator = 1000; + uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; + // loop over all the samples + for (uint32_t j = 0; j < n; ++j) { + size_t i = oldestClosed++ & (mSamplingN - 1); + uint32_t wallNs = mMonotonicNs[i]; + if (tail != NULL) { + tail[j] = wallNs; + } + wall.sample(wallNs); + uint32_t sampleLoadNs = mLoadNs[i]; + loadNs.sample(sampleLoadNs); +#ifdef CPU_FREQUENCY_STATISTICS + uint32_t sampleCpukHz = mCpukHz[i]; + // skip bad kHz samples + if ((sampleCpukHz & ~0xF) != 0) { + kHz.sample(sampleCpukHz >> 4); + if (sampleCpukHz == previousCpukHz) { + double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; + double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 + loadMHz.sample(adjMHz); + } + } + previousCpukHz = sampleCpukHz; +#endif + } + if (n) { + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(fd, " wall clock time in ms per mix cycle:\n" + " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, + wall.stddev()*1e-6); + dprintf(fd, " raw CPU load in us per mix cycle:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, + loadNs.stddev()*1e-3); + } else { + dprintf(fd, " No FastMixer statistics available currently\n"); + } +#ifdef CPU_FREQUENCY_STATISTICS + dprintf(fd, " CPU clock frequency in MHz:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); + dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" + " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", + loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); +#endif + if (tail != NULL) { + qsort(tail, n, sizeof(uint32_t), compare_uint32_t); + // assume same number of tail samples on each side, left and right + uint32_t count = n / kTailDenominator; + CentralTendencyStatistics left, right; + for (uint32_t i = 0; i < count; ++i) { + left.sample(tail[i]); + right.sample(tail[n - (i + 1)]); + } + dprintf(fd, " Distribution of mix cycle times in ms for the tails " + "(> ~3 stddev outliers):\n" + " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" + " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, + right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, + right.stddev()*1e-6); + delete[] tail; + } +#endif + // The active track mask and track states are updated non-atomically. + // So if we relied on isActive to decide whether to display, + // then we might display an obsolete track or omit an active track. + // Instead we always display all tracks, with an indication + // of whether we think the track is active. + uint32_t trackMask = mTrackMask; + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + FastMixerState::kMaxFastTracks, trackMask); + dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); + for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { + bool isActive = trackMask & 1; + const FastTrackDump *ftDump = &mTracks[i]; + const FastTrackUnderruns& underruns = ftDump->mUnderruns; + const char *mostRecent; + switch (underruns.mBitFields.mMostRecent) { + case UNDERRUN_FULL: + mostRecent = "full"; + break; + case UNDERRUN_PARTIAL: + mostRecent = "partial"; + break; + case UNDERRUN_EMPTY: + mostRecent = "empty"; + break; + default: + mostRecent = "?"; + break; + } + dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", + (underruns.mBitFields.mFull) & UNDERRUN_MASK, + (underruns.mBitFields.mPartial) & UNDERRUN_MASK, + (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, + mostRecent, ftDump->mFramesReady); + } +} + +} // android diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h index 6a1e464..ac15e7c 100644 --- a/services/audioflinger/FastMixerDumpState.h +++ b/services/audioflinger/FastMixerDumpState.h @@ -17,7 +17,10 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H #define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H +#include <stdint.h> #include "Configuration.h" +#include "FastThreadDumpState.h" +#include "FastMixerState.h" namespace android { @@ -52,22 +55,12 @@ private: struct FastTrackDump { FastTrackDump() : mFramesReady(0) { } /*virtual*/ ~FastTrackDump() { } - FastTrackUnderruns mUnderruns; - size_t mFramesReady; // most recent value only; no long-term statistics kept + FastTrackUnderruns mUnderruns; + size_t mFramesReady; // most recent value only; no long-term statistics kept }; -// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. -// Each individual native word-sized field is accessed atomically. But the -// overall structure is non-atomic, that is there may be an inconsistency between fields. -// No barriers or locks are used for either writing or reading. -// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). -// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. struct FastMixerDumpState : FastThreadDumpState { - FastMixerDumpState( -#ifdef FAST_MIXER_STATISTICS - uint32_t samplingN = kSamplingNforLowRamDevice -#endif - ); + FastMixerDumpState(); /*virtual*/ ~FastMixerDumpState(); void dump(int fd) const; // should only be called on a stable copy, not the original @@ -80,14 +73,6 @@ struct FastMixerDumpState : FastThreadDumpState { size_t mFrameCount; uint32_t mTrackMask; // mask of active tracks FastTrackDump mTracks[FastMixerState::kMaxFastTracks]; - -#ifdef FAST_MIXER_STATISTICS - // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. - // This value was chosen such that each array uses 1 small page (4 Kbytes). - static const uint32_t kSamplingNforLowRamDevice = 0x400; - // Increase sampling window after construction, must be a power of 2 <= kSamplingN - void increaseSamplingN(uint32_t samplingN); -#endif }; } // android diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 3aa8dad..a8c2634 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -39,4 +39,19 @@ FastMixerState::~FastMixerState() { } +// static +const char *FastMixerState::commandToString(Command command) +{ + const char *str = FastThreadState::commandToString(command); + if (str != NULL) { + return str; + } + switch (command) { + case FastMixerState::MIX: return "MIX"; + case FastMixerState::WRITE: return "WRITE"; + case FastMixerState::MIX_WRITE: return "MIX_WRITE"; + } + LOG_ALWAYS_FATAL("%s", __func__); +} + } // namespace android diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 661c9ca..916514f 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -73,6 +73,9 @@ struct FastMixerState : FastThreadState { // This might be a one-time configuration rather than per-state NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink + + // never returns NULL; asserts if command is invalid + static const char *commandToString(Command command); }; // struct FastMixerState } // namespace android diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp index 216dace..5ca579b 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -25,54 +25,58 @@ #include <utils/Log.h> #include <utils/Trace.h> #include "FastThread.h" +#include "FastThreadDumpState.h" #define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep #define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling -#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup +#define MIN_WARMUP_CYCLES 2 // minimum number of consecutive in-range loop cycles + // to wait for warmup #define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup namespace android { FastThread::FastThread() : Thread(false /*canCallJava*/), - // re-initialized to &initial by subclass constructor - previous(NULL), current(NULL), - /* oldTs({0, 0}), */ - oldTsValid(false), - sleepNs(-1), - periodNs(0), - underrunNs(0), - overrunNs(0), - forceNs(0), - warmupNs(0), - // re-initialized to &dummyDumpState by subclass constructor + // re-initialized to &sInitial by subclass constructor + mPrevious(NULL), mCurrent(NULL), + /* mOldTs({0, 0}), */ + mOldTsValid(false), + mSleepNs(-1), + mPeriodNs(0), + mUnderrunNs(0), + mOverrunNs(0), + mForceNs(0), + mWarmupNsMin(0), + mWarmupNsMax(LONG_MAX), + // re-initialized to &mDummySubclassDumpState by subclass constructor mDummyDumpState(NULL), - dumpState(NULL), - ignoreNextOverrun(true), -#ifdef FAST_MIXER_STATISTICS - // oldLoad - oldLoadValid(false), - bounds(0), - full(false), - // tcu + mDumpState(NULL), + mIgnoreNextOverrun(true), +#ifdef FAST_THREAD_STATISTICS + // mOldLoad + mOldLoadValid(false), + mBounds(0), + mFull(false), + // mTcu #endif - coldGen(0), - isWarm(false), - /* measuredWarmupTs({0, 0}), */ - warmupCycles(0), - // dummyLogWriter - logWriter(&dummyLogWriter), - timestampStatus(INVALID_OPERATION), + mColdGen(0), + mIsWarm(false), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0), + mWarmupConsecutiveInRangeCycles(0), + // mDummyLogWriter + mLogWriter(&mDummyLogWriter), + mTimestampStatus(INVALID_OPERATION), - command(FastThreadState::INITIAL), + mCommand(FastThreadState::INITIAL), #if 0 frameCount(0), #endif - attemptedWrite(false) + mAttemptedWrite(false) { - oldTs.tv_sec = 0; - oldTs.tv_nsec = 0; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; + mOldTs.tv_sec = 0; + mOldTs.tv_nsec = 0; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; } FastThread::~FastThread() @@ -84,34 +88,34 @@ bool FastThread::threadLoop() for (;;) { // either nanosleep, sched_yield, or busy wait - if (sleepNs >= 0) { - if (sleepNs > 0) { - ALOG_ASSERT(sleepNs < 1000000000); - const struct timespec req = {0, sleepNs}; + if (mSleepNs >= 0) { + if (mSleepNs > 0) { + ALOG_ASSERT(mSleepNs < 1000000000); + const struct timespec req = {0, mSleepNs}; nanosleep(&req, NULL); } else { sched_yield(); } } // default to long sleep for next cycle - sleepNs = FAST_DEFAULT_NS; + mSleepNs = FAST_DEFAULT_NS; // poll for state change const FastThreadState *next = poll(); if (next == NULL) { // continue to use the default initial state until a real state is available - // FIXME &initial not available, should save address earlier - //ALOG_ASSERT(current == &initial && previous == &initial); - next = current; + // FIXME &sInitial not available, should save address earlier + //ALOG_ASSERT(mCurrent == &sInitial && previous == &sInitial); + next = mCurrent; } - command = next->mCommand; - if (next != current) { + mCommand = next->mCommand; + if (next != mCurrent) { // As soon as possible of learning of a new dump area, start using it - dumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; - logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter; - setLog(logWriter); + mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; + mLogWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &mDummyLogWriter; + setLog(mLogWriter); // We want to always have a valid reference to the previous (non-idle) state. // However, the state queue only guarantees access to current and previous states. @@ -122,37 +126,38 @@ bool FastThread::threadLoop() // non-idle -> idle update previous from copy of current // idle -> idle don't update previous // idle -> non-idle don't update previous - if (!(current->mCommand & FastThreadState::IDLE)) { - if (command & FastThreadState::IDLE) { + if (!(mCurrent->mCommand & FastThreadState::IDLE)) { + if (mCommand & FastThreadState::IDLE) { onIdle(); - oldTsValid = false; -#ifdef FAST_MIXER_STATISTICS - oldLoadValid = false; + mOldTsValid = false; +#ifdef FAST_THREAD_STATISTICS + mOldLoadValid = false; #endif - ignoreNextOverrun = true; + mIgnoreNextOverrun = true; } - previous = current; + mPrevious = mCurrent; } - current = next; + mCurrent = next; } #if !LOG_NDEBUG next = NULL; // not referenced again #endif - dumpState->mCommand = command; + mDumpState->mCommand = mCommand; + // FIXME what does this comment mean? // << current, previous, command, dumpState >> - switch (command) { + switch (mCommand) { case FastThreadState::INITIAL: case FastThreadState::HOT_IDLE: - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; continue; case FastThreadState::COLD_IDLE: // only perform a cold idle command once // FIXME consider checking previous state and only perform if previous != COLD_IDLE - if (current->mColdGen != coldGen) { - int32_t *coldFutexAddr = current->mColdFutexAddr; + if (mCurrent->mColdGen != mColdGen) { + int32_t *coldFutexAddr = mCurrent->mColdFutexAddr; ALOG_ASSERT(coldFutexAddr != NULL); int32_t old = android_atomic_dec(coldFutexAddr); if (old <= 0) { @@ -164,41 +169,42 @@ bool FastThread::threadLoop() } // This may be overly conservative; there could be times that the normal mixer // requests such a brief cold idle that it doesn't require resetting this flag. - isWarm = false; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; - warmupCycles = 0; - sleepNs = -1; - coldGen = current->mColdGen; -#ifdef FAST_MIXER_STATISTICS - bounds = 0; - full = false; + mIsWarm = false; + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; + mWarmupCycles = 0; + mWarmupConsecutiveInRangeCycles = 0; + mSleepNs = -1; + mColdGen = mCurrent->mColdGen; +#ifdef FAST_THREAD_STATISTICS + mBounds = 0; + mFull = false; #endif - oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs); - timestampStatus = INVALID_OPERATION; + mOldTsValid = !clock_gettime(CLOCK_MONOTONIC, &mOldTs); + mTimestampStatus = INVALID_OPERATION; } else { - sleepNs = FAST_HOT_IDLE_NS; + mSleepNs = FAST_HOT_IDLE_NS; } continue; case FastThreadState::EXIT: onExit(); return false; default: - LOG_ALWAYS_FATAL_IF(!isSubClassCommand(command)); + LOG_ALWAYS_FATAL_IF(!isSubClassCommand(mCommand)); break; } // there is a non-idle state available to us; did the state change? - if (current != previous) { + if (mCurrent != mPrevious) { onStateChange(); #if 1 // FIXME shouldn't need this // only process state change once - previous = current; + mPrevious = mCurrent; #endif } // do work using current state here - attemptedWrite = false; + mAttemptedWrite = false; onWork(); // To be exactly periodic, compute the next sleep time based on current time. @@ -207,13 +213,13 @@ bool FastThread::threadLoop() struct timespec newTs; int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); if (rc == 0) { - //logWriter->logTimestamp(newTs); - if (oldTsValid) { - time_t sec = newTs.tv_sec - oldTs.tv_sec; - long nsec = newTs.tv_nsec - oldTs.tv_nsec; + //mLogWriter->logTimestamp(newTs); + if (mOldTsValid) { + time_t sec = newTs.tv_sec - mOldTs.tv_sec; + long nsec = newTs.tv_nsec - mOldTs.tv_nsec; ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0), "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld", - oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); + mOldTs.tv_sec, mOldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); if (nsec < 0) { --sec; nsec += 1000000000; @@ -221,62 +227,70 @@ bool FastThread::threadLoop() // To avoid an initial underrun on fast tracks after exiting standby, // do not start pulling data from tracks and mixing until warmup is complete. // Warmup is considered complete after the earlier of: - // MIN_WARMUP_CYCLES write() attempts and last one blocks for at least warmupNs + // MIN_WARMUP_CYCLES consecutive in-range write() attempts, + // where "in-range" means mWarmupNsMin <= cycle time <= mWarmupNsMax // MAX_WARMUP_CYCLES write() attempts. // This is overly conservative, but to get better accuracy requires a new HAL API. - if (!isWarm && attemptedWrite) { - measuredWarmupTs.tv_sec += sec; - measuredWarmupTs.tv_nsec += nsec; - if (measuredWarmupTs.tv_nsec >= 1000000000) { - measuredWarmupTs.tv_sec++; - measuredWarmupTs.tv_nsec -= 1000000000; + if (!mIsWarm && mAttemptedWrite) { + mMeasuredWarmupTs.tv_sec += sec; + mMeasuredWarmupTs.tv_nsec += nsec; + if (mMeasuredWarmupTs.tv_nsec >= 1000000000) { + mMeasuredWarmupTs.tv_sec++; + mMeasuredWarmupTs.tv_nsec -= 1000000000; } - ++warmupCycles; - if ((nsec > warmupNs && warmupCycles >= MIN_WARMUP_CYCLES) || - (warmupCycles >= MAX_WARMUP_CYCLES)) { - isWarm = true; - dumpState->mMeasuredWarmupTs = measuredWarmupTs; - dumpState->mWarmupCycles = warmupCycles; + ++mWarmupCycles; + if (mWarmupNsMin <= nsec && nsec <= mWarmupNsMax) { + ALOGV("warmup cycle %d in range: %.03f ms", mWarmupCycles, nsec * 1e-9); + ++mWarmupConsecutiveInRangeCycles; + } else { + ALOGV("warmup cycle %d out of range: %.03f ms", mWarmupCycles, nsec * 1e-9); + mWarmupConsecutiveInRangeCycles = 0; + } + if ((mWarmupConsecutiveInRangeCycles >= MIN_WARMUP_CYCLES) || + (mWarmupCycles >= MAX_WARMUP_CYCLES)) { + mIsWarm = true; + mDumpState->mMeasuredWarmupTs = mMeasuredWarmupTs; + mDumpState->mWarmupCycles = mWarmupCycles; } } - sleepNs = -1; - if (isWarm) { - if (sec > 0 || nsec > underrunNs) { + mSleepNs = -1; + if (mIsWarm) { + if (sec > 0 || nsec > mUnderrunNs) { ATRACE_NAME("underrun"); // FIXME only log occasionally ALOGV("underrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mUnderruns++; - ignoreNextOverrun = true; - } else if (nsec < overrunNs) { - if (ignoreNextOverrun) { - ignoreNextOverrun = false; + mDumpState->mUnderruns++; + mIgnoreNextOverrun = true; + } else if (nsec < mOverrunNs) { + if (mIgnoreNextOverrun) { + mIgnoreNextOverrun = false; } else { // FIXME only log occasionally ALOGV("overrun: time since last cycle %d.%03ld sec", (int) sec, nsec / 1000000L); - dumpState->mOverruns++; + mDumpState->mOverruns++; } // This forces a minimum cycle time. It: // - compensates for an audio HAL with jitter due to sample rate conversion // - works with a variable buffer depth audio HAL that never pulls at a - // rate < than overrunNs per buffer. + // rate < than mOverrunNs per buffer. // - recovers from overrun immediately after underrun // It doesn't work with a non-blocking audio HAL. - sleepNs = forceNs - nsec; + mSleepNs = mForceNs - nsec; } else { - ignoreNextOverrun = false; + mIgnoreNextOverrun = false; } } -#ifdef FAST_MIXER_STATISTICS - if (isWarm) { +#ifdef FAST_THREAD_STATISTICS + if (mIsWarm) { // advance the FIFO queue bounds - size_t i = bounds & (dumpState->mSamplingN - 1); - bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); - if (full) { - bounds += 0x10000; - } else if (!(bounds & (dumpState->mSamplingN - 1))) { - full = true; + size_t i = mBounds & (mDumpState->mSamplingN - 1); + mBounds = (mBounds & 0xFFFF0000) | ((mBounds + 1) & 0xFFFF); + if (mFull) { + mBounds += 0x10000; + } else if (!(mBounds & (mDumpState->mSamplingN - 1))) { + mFull = true; } // compute the delta value of clock_gettime(CLOCK_MONOTONIC) uint32_t monotonicNs = nsec; @@ -288,9 +302,9 @@ bool FastThread::threadLoop() struct timespec newLoad; rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad); if (rc == 0) { - if (oldLoadValid) { - sec = newLoad.tv_sec - oldLoad.tv_sec; - nsec = newLoad.tv_nsec - oldLoad.tv_nsec; + if (mOldLoadValid) { + sec = newLoad.tv_sec - mOldLoad.tv_sec; + nsec = newLoad.tv_nsec - mOldLoad.tv_nsec; if (nsec < 0) { --sec; nsec += 1000000000; @@ -301,42 +315,42 @@ bool FastThread::threadLoop() } } else { // first time through the loop - oldLoadValid = true; + mOldLoadValid = true; } - oldLoad = newLoad; + mOldLoad = newLoad; } #ifdef CPU_FREQUENCY_STATISTICS // get the absolute value of CPU clock frequency in kHz int cpuNum = sched_getcpu(); - uint32_t kHz = tcu.getCpukHz(cpuNum); + uint32_t kHz = mTcu.getCpukHz(cpuNum); kHz = (kHz << 4) | (cpuNum & 0xF); #endif // save values in FIFO queues for dumpsys // these stores #1, #2, #3 are not atomic with respect to each other, // or with respect to store #4 below - dumpState->mMonotonicNs[i] = monotonicNs; - dumpState->mLoadNs[i] = loadNs; + mDumpState->mMonotonicNs[i] = monotonicNs; + mDumpState->mLoadNs[i] = loadNs; #ifdef CPU_FREQUENCY_STATISTICS - dumpState->mCpukHz[i] = kHz; + mDumpState->mCpukHz[i] = kHz; #endif // this store #4 is not atomic with respect to stores #1, #2, #3 above, but // the newest open & oldest closed halves are atomic with respect to each other - dumpState->mBounds = bounds; + mDumpState->mBounds = mBounds; ATRACE_INT("cycle_ms", monotonicNs / 1000000); ATRACE_INT("load_us", loadNs / 1000); } #endif } else { // first time through the loop - oldTsValid = true; - sleepNs = periodNs; - ignoreNextOverrun = true; + mOldTsValid = true; + mSleepNs = mPeriodNs; + mIgnoreNextOverrun = true; } - oldTs = newTs; + mOldTs = newTs; } else { // monotonic clock is broken - oldTsValid = false; - sleepNs = periodNs; + mOldTsValid = false; + mSleepNs = mPeriodNs; } } // for (;;) diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h index 1330334..2efb6de 100644 --- a/services/audioflinger/FastThread.h +++ b/services/audioflinger/FastThread.h @@ -48,42 +48,45 @@ protected: virtual void onStateChange() = 0; virtual void onWork() = 0; - // FIXME these former local variables need comments and to be renamed to have an "m" prefix - const FastThreadState *previous; - const FastThreadState *current; - struct timespec oldTs; - bool oldTsValid; - long sleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep - long periodNs; // expected period; the time required to render one mix buffer - long underrunNs; // underrun likely when write cycle is greater than this value - long overrunNs; // overrun likely when write cycle is less than this value - long forceNs; // if overrun detected, force the write cycle to take this much time - long warmupNs; // warmup complete when write cycle is greater than to this value - FastThreadDumpState *mDummyDumpState; - FastThreadDumpState *dumpState; - bool ignoreNextOverrun; // used to ignore initial overrun and first after an underrun -#ifdef FAST_MIXER_STATISTICS - struct timespec oldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) - bool oldLoadValid; // whether oldLoad is valid - uint32_t bounds; - bool full; // whether we have collected at least mSamplingN samples + // FIXME these former local variables need comments + const FastThreadState* mPrevious; + const FastThreadState* mCurrent; + struct timespec mOldTs; + bool mOldTsValid; + long mSleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep + long mPeriodNs; // expected period; the time required to render one mix buffer + long mUnderrunNs; // underrun likely when write cycle is greater than this value + long mOverrunNs; // overrun likely when write cycle is less than this value + long mForceNs; // if overrun detected, + // force the write cycle to take this much time + long mWarmupNsMin; // warmup complete when write cycle is greater than or equal to + // this value + long mWarmupNsMax; // and less than or equal to this value + FastThreadDumpState* mDummyDumpState; + FastThreadDumpState* mDumpState; + bool mIgnoreNextOverrun; // used to ignore initial overrun and first after an + // underrun +#ifdef FAST_THREAD_STATISTICS + struct timespec mOldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) + bool mOldLoadValid; // whether oldLoad is valid + uint32_t mBounds; + bool mFull; // whether we have collected at least mSamplingN samples #ifdef CPU_FREQUENCY_STATISTICS - ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz + ThreadCpuUsage mTcu; // for reading the current CPU clock frequency in kHz #endif #endif - unsigned coldGen; // last observed mColdGen - bool isWarm; // true means ready to mix, false means wait for warmup before mixing - struct timespec measuredWarmupTs; // how long did it take for warmup to complete - uint32_t warmupCycles; // counter of number of loop cycles required to warmup - NBLog::Writer dummyLogWriter; - NBLog::Writer *logWriter; - status_t timestampStatus; + unsigned mColdGen; // last observed mColdGen + bool mIsWarm; // true means ready to mix, + // false means wait for warmup before mixing + struct timespec mMeasuredWarmupTs; // how long did it take for warmup to complete + uint32_t mWarmupCycles; // counter of number of loop cycles during warmup phase + uint32_t mWarmupConsecutiveInRangeCycles; // number of consecutive cycles in range + NBLog::Writer mDummyLogWriter; + NBLog::Writer* mLogWriter; + status_t mTimestampStatus; - FastThreadState::Command command; -#if 0 - size_t frameCount; -#endif - bool attemptedWrite; + FastThreadState::Command mCommand; + bool mAttemptedWrite; }; // class FastThread diff --git a/services/audioflinger/FastThreadDumpState.cpp b/services/audioflinger/FastThreadDumpState.cpp new file mode 100644 index 0000000..9df5c4c --- /dev/null +++ b/services/audioflinger/FastThreadDumpState.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "FastThreadDumpState.h" + +namespace android { + +FastThreadDumpState::FastThreadDumpState() : + mCommand(FastThreadState::INITIAL), mUnderruns(0), mOverruns(0), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0) +#ifdef FAST_THREAD_STATISTICS + , mSamplingN(0), mBounds(0) +#endif +{ + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; +#ifdef FAST_THREAD_STATISTICS + increaseSamplingN(1); +#endif +} + +FastThreadDumpState::~FastThreadDumpState() +{ +} + +#ifdef FAST_THREAD_STATISTICS +void FastThreadDumpState::increaseSamplingN(uint32_t samplingN) +{ + if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { + return; + } + uint32_t additional = samplingN - mSamplingN; + // sample arrays aren't accessed atomically with respect to the bounds, + // so clearing reduces chance for dumpsys to read random uninitialized samples + memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); + memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); +#ifdef CPU_FREQUENCY_STATISTICS + memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); +#endif + mSamplingN = samplingN; +} +#endif + +} // android diff --git a/services/audioflinger/FastThreadDumpState.h b/services/audioflinger/FastThreadDumpState.h new file mode 100644 index 0000000..1ce0914 --- /dev/null +++ b/services/audioflinger/FastThreadDumpState.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_AUDIO_FAST_THREAD_DUMP_STATE_H +#define ANDROID_AUDIO_FAST_THREAD_DUMP_STATE_H + +#include "Configuration.h" +#include "FastThreadState.h" + +namespace android { + +// The FastThreadDumpState keeps a cache of FastThread statistics that can be logged by dumpsys. +// Each individual native word-sized field is accessed atomically. But the +// overall structure is non-atomic, that is there may be an inconsistency between fields. +// No barriers or locks are used for either writing or reading. +// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). +// It has a different lifetime than the FastThread, and so it can't be a member of FastThread. +struct FastThreadDumpState { + FastThreadDumpState(); + /*virtual*/ ~FastThreadDumpState(); + + FastThreadState::Command mCommand; // current command + uint32_t mUnderruns; // total number of underruns + uint32_t mOverruns; // total number of overruns + struct timespec mMeasuredWarmupTs; // measured warmup time + uint32_t mWarmupCycles; // number of loop cycles required to warmup + +#ifdef FAST_THREAD_STATISTICS + // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. + // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. + // The sample arrays are virtually allocated based on this compile-time constant, + // but are only initialized and used based on the runtime parameter mSamplingN. + static const uint32_t kSamplingN = 0x8000; + // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. + // This value was chosen such that each array uses 1 small page (4 Kbytes). + static const uint32_t kSamplingNforLowRamDevice = 0x400; + // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. + uint32_t mSamplingN; + // The bounds define the interval of valid samples, and are represented as follows: + // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N + // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N + // Number of valid samples is newest - oldest. + uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz + // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. + uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time + uint32_t mLoadNs[kSamplingN]; // delta CPU load in time +#ifdef CPU_FREQUENCY_STATISTICS + uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# +#endif + + // Increase sampling window after construction, must be a power of 2 <= kSamplingN + void increaseSamplingN(uint32_t samplingN); +#endif + +}; // struct FastThreadDumpState + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_DUMP_STATE_H diff --git a/services/audioflinger/FastThreadState.cpp b/services/audioflinger/FastThreadState.cpp index 6994872..ad5f31f 100644 --- a/services/audioflinger/FastThreadState.cpp +++ b/services/audioflinger/FastThreadState.cpp @@ -29,21 +29,16 @@ FastThreadState::~FastThreadState() { } - -FastThreadDumpState::FastThreadDumpState() : - mCommand(FastThreadState::INITIAL), mUnderruns(0), mOverruns(0), - /* mMeasuredWarmupTs({0, 0}), */ - mWarmupCycles(0) -#ifdef FAST_MIXER_STATISTICS - , mSamplingN(1), mBounds(0) -#endif -{ - mMeasuredWarmupTs.tv_sec = 0; - mMeasuredWarmupTs.tv_nsec = 0; -} - -FastThreadDumpState::~FastThreadDumpState() +// static +const char *FastThreadState::commandToString(FastThreadState::Command command) { + switch (command) { + case FastThreadState::INITIAL: return "INITIAL"; + case FastThreadState::HOT_IDLE: return "HOT_IDLE"; + case FastThreadState::COLD_IDLE: return "COLD_IDLE"; + case FastThreadState::EXIT: return "EXIT"; + } + return NULL; } } // namespace android diff --git a/services/audioflinger/FastThreadState.h b/services/audioflinger/FastThreadState.h index 1ab8a0a..f18f846 100644 --- a/services/audioflinger/FastThreadState.h +++ b/services/audioflinger/FastThreadState.h @@ -46,43 +46,10 @@ struct FastThreadState { FastThreadDumpState* mDumpState; // if non-NULL, then update dump state periodically NBLog::Writer* mNBLogWriter; // non-blocking logger + // returns NULL if command belongs to a subclass + static const char *commandToString(Command command); }; // struct FastThreadState - -// FIXME extract common part of comment at FastMixerDumpState -struct FastThreadDumpState { - FastThreadDumpState(); - /*virtual*/ ~FastThreadDumpState(); - - FastThreadState::Command mCommand; // current command - uint32_t mUnderruns; // total number of underruns - uint32_t mOverruns; // total number of overruns - struct timespec mMeasuredWarmupTs; // measured warmup time - uint32_t mWarmupCycles; // number of loop cycles required to warmup - -#ifdef FAST_MIXER_STATISTICS - // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. - // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. - // The sample arrays are virtually allocated based on this compile-time constant, - // but are only initialized and used based on the runtime parameter mSamplingN. - static const uint32_t kSamplingN = 0x8000; - // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. - uint32_t mSamplingN; - // The bounds define the interval of valid samples, and are represented as follows: - // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N - // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N - // Number of valid samples is newest - oldest. - uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz - // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. - uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time - uint32_t mLoadNs[kSamplingN]; // delta CPU load in time -#ifdef CPU_FREQUENCY_STATISTICS - uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# -#endif -#endif - -}; // struct FastThreadDumpState - } // android #endif // ANDROID_AUDIO_FAST_THREAD_STATE_H diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index 4f0c6b1..efbdcff 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -694,4 +694,4 @@ status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_co } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index ee48276..45df6a9 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -157,8 +157,9 @@ private: bool mFlushHwPending; // track requests for thread flush // for last call to getTimestamp - bool mPreviousValid; - uint32_t mPreviousFramesWritten; + bool mPreviousTimestampValid; + // This is either the first timestamp or one that has passed + // the check to prevent retrograde motion. AudioTimestamp mPreviousTimestamp; }; // end of Track @@ -255,7 +256,7 @@ public: class Buffer : public AudioBufferProvider::Buffer { public: - int16_t *mBuffer; + void *mBuffer; }; OutputTrack(PlaybackThread *thread, @@ -271,7 +272,7 @@ public: AudioSystem::SYNC_EVENT_NONE, int triggerSession = 0); virtual void stop(); - bool write(int16_t* data, uint32_t frames); + bool write(void* data, uint32_t frames); bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; } bool isActive() const { return mActive; } const wp<ThreadBase>& thread() const { return mThread; } diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp new file mode 100644 index 0000000..d23588e --- /dev/null +++ b/services/audioflinger/SpdifStreamOut.cpp @@ -0,0 +1,166 @@ +/* +** +** Copyright 2015, 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 "AudioFlinger" +//#define LOG_NDEBUG 0 +#include <hardware/audio.h> +#include <utils/Log.h> + +#include <audio_utils/spdif/SPDIFEncoder.h> + +#include "AudioHwDevice.h" +#include "AudioStreamOut.h" +#include "SpdifStreamOut.h" + +namespace android { + +/** + * If the AudioFlinger is processing encoded data and the HAL expects + * PCM then we need to wrap the data in an SPDIF wrapper. + */ +SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags) + : AudioStreamOut(dev,flags) + , mRateMultiplier(1) + , mSpdifEncoder(this) + , mRenderPositionHal(0) + , mPreviousHalPosition32(0) +{ +} + +status_t SpdifStreamOut::open( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + const char *address) +{ + struct audio_config customConfig = *config; + + customConfig.format = AUDIO_FORMAT_PCM_16_BIT; + customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO; + + // Some data bursts run at a higher sample rate. + switch(config->format) { + case AUDIO_FORMAT_E_AC3: + mRateMultiplier = 4; + break; + case AUDIO_FORMAT_AC3: + mRateMultiplier = 1; + break; + default: + ALOGE("ERROR SpdifStreamOut::open() unrecognized format 0x%08X\n", + config->format); + return BAD_VALUE; + } + customConfig.sample_rate = config->sample_rate * mRateMultiplier; + + // Always print this because otherwise it could be very confusing if the + // HAL and AudioFlinger are using different formats. + // Print before open() because HAL may modify customConfig. + ALOGI("SpdifStreamOut::open() AudioFlinger requested" + " sampleRate %d, format %#x, channelMask %#x", + config->sample_rate, + config->format, + config->channel_mask); + ALOGI("SpdifStreamOut::open() HAL configured for" + " sampleRate %d, format %#x, channelMask %#x", + customConfig.sample_rate, + customConfig.format, + customConfig.channel_mask); + + status_t status = AudioStreamOut::open( + handle, + devices, + &customConfig, + address); + + ALOGI("SpdifStreamOut::open() status = %d", status); + + return status; +} + +// Account for possibly higher sample rate. +status_t SpdifStreamOut::getRenderPosition(uint32_t *frames) +{ + uint32_t halPosition = 0; + status_t status = AudioStreamOut::getRenderPosition(&halPosition); + if (status != NO_ERROR) { + return status; + } + + // Accumulate a 64-bit position so that we wrap at the right place. + if (mRateMultiplier != 1) { + // Maintain a 64-bit render position. + int32_t deltaHalPosition = (int32_t)(halPosition - mPreviousHalPosition32); + mPreviousHalPosition32 = halPosition; + mRenderPositionHal += deltaHalPosition; + + // Scale from device sample rate to application rate. + uint64_t renderPositionApp = mRenderPositionHal / mRateMultiplier; + ALOGV("SpdifStreamOut::getRenderPosition() " + "renderPositionAppRate = %llu = %llu / %u\n", + renderPositionApp, mRenderPositionHal, mRateMultiplier); + + *frames = (uint32_t)renderPositionApp; + } else { + *frames = halPosition; + } + return status; +} + +int SpdifStreamOut::flush() +{ + // FIXME Is there an issue here with flush being asynchronous? + mRenderPositionHal = 0; + mPreviousHalPosition32 = 0; + return AudioStreamOut::flush(); +} + +int SpdifStreamOut::standby() +{ + mRenderPositionHal = 0; + mPreviousHalPosition32 = 0; + return AudioStreamOut::standby(); +} + +// Account for possibly higher sample rate. +// This is much easier when all the values are 64-bit. +status_t SpdifStreamOut::getPresentationPosition(uint64_t *frames, + struct timespec *timestamp) +{ + uint64_t halFrames = 0; + status_t status = AudioStreamOut::getPresentationPosition(&halFrames, timestamp); + *frames = halFrames / mRateMultiplier; + return status; +} + +size_t SpdifStreamOut::getFrameSize() +{ + return sizeof(int8_t); +} + +ssize_t SpdifStreamOut::writeDataBurst(const void* buffer, size_t bytes) +{ + return AudioStreamOut::write(buffer, bytes); +} + +ssize_t SpdifStreamOut::write(const void* buffer, size_t bytes) +{ + // Write to SPDIF wrapper. It will call back to writeDataBurst(). + return mSpdifEncoder.write(buffer, bytes); +} + +} // namespace android diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h new file mode 100644 index 0000000..cb82ac7 --- /dev/null +++ b/services/audioflinger/SpdifStreamOut.h @@ -0,0 +1,107 @@ +/* +** +** Copyright 2015, 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. +*/ + +#ifndef ANDROID_SPDIF_STREAM_OUT_H +#define ANDROID_SPDIF_STREAM_OUT_H + +#include <stdint.h> +#include <sys/types.h> + +#include <system/audio.h> + +#include "AudioHwDevice.h" +#include "AudioStreamOut.h" +#include "SpdifStreamOut.h" + +#include <audio_utils/spdif/SPDIFEncoder.h> + +namespace android { + +/** + * Stream that is a PCM data burst in the HAL but looks like an encoded stream + * to the AudioFlinger. Wraps encoded data in an SPDIF wrapper per IEC61973-3. + */ +class SpdifStreamOut : public AudioStreamOut { +public: + + SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags); + + virtual ~SpdifStreamOut() { } + + virtual status_t open( + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + const char *address); + + virtual status_t getRenderPosition(uint32_t *frames); + + virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); + + /** + * Write audio buffer to driver. Returns number of bytes written, or a + * negative status_t. If at least one frame was written successfully prior to the error, + * it is suggested that the driver return that successful (short) byte count + * and then return an error in the subsequent call. + * + * If set_callback() has previously been called to enable non-blocking mode + * the write() is not allowed to block. It must write only the number of + * bytes that currently fit in the driver/hardware buffer and then return + * this byte count. If this is less than the requested write size the + * callback function must be called when more space is available in the + * driver/hardware buffer. + */ + virtual ssize_t write(const void* buffer, size_t bytes); + + virtual size_t getFrameSize(); + + virtual status_t flush(); + virtual status_t standby(); + +private: + + class MySPDIFEncoder : public SPDIFEncoder + { + public: + MySPDIFEncoder(SpdifStreamOut *spdifStreamOut) + : mSpdifStreamOut(spdifStreamOut) + { + } + + virtual ssize_t writeOutput(const void* buffer, size_t bytes) + { + return mSpdifStreamOut->writeDataBurst(buffer, bytes); + } + protected: + SpdifStreamOut * const mSpdifStreamOut; + }; + + int mRateMultiplier; + MySPDIFEncoder mSpdifEncoder; + + // Used to implement getRenderPosition() + int64_t mRenderPositionHal; + uint32_t mPreviousHalPosition32; + + ssize_t writeDataBurst(const void* data, size_t bytes); + ssize_t writeInternal(const void* buffer, size_t bytes); + +}; + +} // namespace android + +#endif // ANDROID_SPDIF_STREAM_OUT_H diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 51025fe..4efb3d7 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -23,7 +23,9 @@ #include "Configuration.h" #include <math.h> #include <fcntl.h> +#include <linux/futex.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <cutils/properties.h> #include <media/AudioParameter.h> #include <media/AudioResamplerPublic.h> @@ -314,6 +316,165 @@ void CpuStats::sample(const String8 &title // ThreadBase // ---------------------------------------------------------------------------- +// static +const char *AudioFlinger::ThreadBase::threadTypeToString(AudioFlinger::ThreadBase::type_t type) +{ + switch (type) { + case MIXER: + return "MIXER"; + case DIRECT: + return "DIRECT"; + case DUPLICATING: + return "DUPLICATING"; + case RECORD: + return "RECORD"; + case OFFLOAD: + return "OFFLOAD"; + default: + return "unknown"; + } +} + +String8 devicesToString(audio_devices_t devices) +{ + static const struct mapping { + audio_devices_t mDevices; + const char * mString; + } mappingsOut[] = { + AUDIO_DEVICE_OUT_EARPIECE, "EARPIECE", + AUDIO_DEVICE_OUT_SPEAKER, "SPEAKER", + AUDIO_DEVICE_OUT_WIRED_HEADSET, "WIRED_HEADSET", + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, "WIRED_HEADPHONE", + AUDIO_DEVICE_OUT_TELEPHONY_TX, "TELEPHONY_TX", + AUDIO_DEVICE_NONE, "NONE", // must be last + }, mappingsIn[] = { + AUDIO_DEVICE_IN_BUILTIN_MIC, "BUILTIN_MIC", + AUDIO_DEVICE_IN_WIRED_HEADSET, "WIRED_HEADSET", + AUDIO_DEVICE_IN_VOICE_CALL, "VOICE_CALL", + AUDIO_DEVICE_IN_REMOTE_SUBMIX, "REMOTE_SUBMIX", + AUDIO_DEVICE_NONE, "NONE", // must be last + }; + String8 result; + audio_devices_t allDevices = AUDIO_DEVICE_NONE; + const mapping *entry; + if (devices & AUDIO_DEVICE_BIT_IN) { + devices &= ~AUDIO_DEVICE_BIT_IN; + entry = mappingsIn; + } else { + entry = mappingsOut; + } + for ( ; entry->mDevices != AUDIO_DEVICE_NONE; entry++) { + allDevices = (audio_devices_t) (allDevices | entry->mDevices); + if (devices & entry->mDevices) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (devices & ~allDevices) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", devices & ~allDevices); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +String8 inputFlagsToString(audio_input_flags_t flags) +{ + static const struct mapping { + audio_input_flags_t mFlag; + const char * mString; + } mappings[] = { + AUDIO_INPUT_FLAG_FAST, "FAST", + AUDIO_INPUT_FLAG_HW_HOTWORD, "HW_HOTWORD", + AUDIO_INPUT_FLAG_NONE, "NONE", // must be last + }; + String8 result; + audio_input_flags_t allFlags = AUDIO_INPUT_FLAG_NONE; + const mapping *entry; + for (entry = mappings; entry->mFlag != AUDIO_INPUT_FLAG_NONE; entry++) { + allFlags = (audio_input_flags_t) (allFlags | entry->mFlag); + if (flags & entry->mFlag) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (flags & ~allFlags) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", flags & ~allFlags); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +String8 outputFlagsToString(audio_output_flags_t flags) +{ + static const struct mapping { + audio_output_flags_t mFlag; + const char * mString; + } mappings[] = { + AUDIO_OUTPUT_FLAG_DIRECT, "DIRECT", + AUDIO_OUTPUT_FLAG_PRIMARY, "PRIMARY", + AUDIO_OUTPUT_FLAG_FAST, "FAST", + AUDIO_OUTPUT_FLAG_DEEP_BUFFER, "DEEP_BUFFER", + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, "COMPRESS_OFFLOAD", + AUDIO_OUTPUT_FLAG_NON_BLOCKING, "NON_BLOCKING", + AUDIO_OUTPUT_FLAG_HW_AV_SYNC, "HW_AV_SYNC", + AUDIO_OUTPUT_FLAG_NONE, "NONE", // must be last + }; + String8 result; + audio_output_flags_t allFlags = AUDIO_OUTPUT_FLAG_NONE; + const mapping *entry; + for (entry = mappings; entry->mFlag != AUDIO_OUTPUT_FLAG_NONE; entry++) { + allFlags = (audio_output_flags_t) (allFlags | entry->mFlag); + if (flags & entry->mFlag) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (flags & ~allFlags) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", flags & ~allFlags); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + +const char *sourceToString(audio_source_t source) +{ + switch (source) { + case AUDIO_SOURCE_DEFAULT: return "default"; + case AUDIO_SOURCE_MIC: return "mic"; + case AUDIO_SOURCE_VOICE_UPLINK: return "voice uplink"; + case AUDIO_SOURCE_VOICE_DOWNLINK: return "voice downlink"; + case AUDIO_SOURCE_VOICE_CALL: return "voice call"; + case AUDIO_SOURCE_CAMCORDER: return "camcorder"; + case AUDIO_SOURCE_VOICE_RECOGNITION: return "voice recognition"; + case AUDIO_SOURCE_VOICE_COMMUNICATION: return "voice communication"; + case AUDIO_SOURCE_REMOTE_SUBMIX: return "remote submix"; + case AUDIO_SOURCE_FM_TUNER: return "FM tuner"; + case AUDIO_SOURCE_HOTWORD: return "hotword"; + default: return "unknown"; + } +} + AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type) : Thread(false /*canCallJava*/), @@ -577,20 +738,22 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - dprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p may be deadlocked\n", this); } + dprintf(fd, " Thread name: %s\n", mThreadName); dprintf(fd, " I/O handle: %d\n", mId); dprintf(fd, " TID: %d\n", getTid()); dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " Sample rate: %u Hz\n", mSampleRate); dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - dprintf(fd, " Channel Count: %u\n", mChannelCount); - dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " Channel count: %u\n", mChannelCount); + dprintf(fd, " Channel mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - dprintf(fd, " Format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); - dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu bytes\n", mFrameSize); dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { @@ -602,6 +765,9 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u } else { dprintf(fd, " none\n"); } + dprintf(fd, " Output device: %#x (%s)\n", mOutDevice, devicesToString(mOutDevice).string()); + dprintf(fd, " Input device: %#x (%s)\n", mInDevice, devicesToString(mInDevice).string()); + dprintf(fd, " Audio source: %d (%s)\n", mAudioSource, sourceToString(mAudioSource)); if (locked) { mLock.unlock(); @@ -635,19 +801,19 @@ void AudioFlinger::ThreadBase::acquireWakeLock(int uid) String16 AudioFlinger::ThreadBase::getWakeLockTag() { switch (mType) { - case MIXER: - return String16("AudioMix"); - case DIRECT: - return String16("AudioDirectOut"); - case DUPLICATING: - return String16("AudioDup"); - case RECORD: - return String16("AudioIn"); - case OFFLOAD: - return String16("AudioOffload"); - default: - ALOG_ASSERT(false); - return String16("AudioUnknown"); + case MIXER: + return String16("AudioMix"); + case DIRECT: + return String16("AudioDirectOut"); + case DUPLICATING: + return String16("AudioDup"); + case RECORD: + return String16("AudioIn"); + case OFFLOAD: + return String16("AudioOffload"); + default: + ALOG_ASSERT(false); + return String16("AudioUnknown"); } } @@ -674,7 +840,7 @@ void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid) if (status == NO_ERROR) { mWakeLockToken = binder; } - ALOGV("acquireWakeLock_l() %s status %d", mName, status); + ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status); } } @@ -687,7 +853,7 @@ void AudioFlinger::ThreadBase::releaseWakeLock() void AudioFlinger::ThreadBase::releaseWakeLock_l() { if (mWakeLockToken != 0) { - ALOGV("releaseWakeLock_l() %s", mName); + ALOGV("releaseWakeLock_l() %s", mThreadName); if (mPowerManager != 0) { mPowerManager->releaseWakeLock(mWakeLockToken, 0, true /* FIXME force oneway contrary to .aidl */); @@ -708,7 +874,7 @@ void AudioFlinger::ThreadBase::getPowerManager_l() { sp<IBinder> binder = defaultServiceManager()->checkService(String16("power")); if (binder == 0) { - ALOGW("Thread %s cannot connect to the power manager service", mName); + ALOGW("Thread %s cannot connect to the power manager service", mThreadName); } else { mPowerManager = interface_cast<IPowerManager>(binder); binder->linkToDeath(mDeathRecipient); @@ -728,7 +894,7 @@ void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uid status_t status; status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array(), true /* FIXME force oneway contrary to .aidl */); - ALOGV("acquireWakeLock_l() %s status %d", mName, status); + ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status); } } @@ -912,7 +1078,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo). if (mType == DIRECT) { ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s", - desc->name, mName); + desc->name, mThreadName); lStatus = BAD_VALUE; goto Exit; } @@ -936,7 +1102,8 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( case DUPLICATING: case RECORD: default: - ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName); + ALOGW("createEffect_l() Cannot add global effect %s on thread %s", + desc->name, mThreadName); lStatus = BAD_VALUE; goto Exit; } @@ -1201,8 +1368,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge // mLatchD, mLatchQ, mLatchDValid(false), mLatchQValid(false) { - snprintf(mName, kNameLength, "AudioOut_%X", id); - mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); + snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id); + mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); // Assumes constructor is called by AudioFlinger with it's mLock held, but // it would be safer to explicitly pass initial masterVolume/masterMute as @@ -1315,7 +1482,10 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, "\nOutput thread %p type %d (%s):\n", this, type(), threadTypeToString(type())); + + dumpBase(fd, args); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); dprintf(fd, " Total writes: %d\n", mNumWrites); @@ -1326,15 +1496,17 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); - - dumpBase(fd, args); + AudioStreamOut *output = mOutput; + audio_output_flags_t flags = output != NULL ? output->flags : AUDIO_OUTPUT_FLAG_NONE; + String8 flagsAsString = outputFlagsToString(flags); + dprintf(fd, " AudioStreamOut: %p flags %#x (%s)\n", output, flags, flagsAsString.string()); } // Thread virtuals void AudioFlinger::PlaybackThread::onFirstRef() { - run(mName, ANDROID_PRIORITY_URGENT_AUDIO); + run(mThreadName, ANDROID_PRIORITY_URGENT_AUDIO); } // ThreadBase virtuals @@ -1378,9 +1550,10 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ( (sharedBuffer != 0) ) || - // use case 2: callback handler and frame count is default or at least as large as HAL + // use case 2: frame count is default or at least as large as HAL ( - (tid != -1) && + // we formerly checked for a callback handler (non-0 tid), + // but that is no longer required for TRANSFER_OBTAIN mode ((frameCount == 0) || (frameCount >= mFrameCount)) ) @@ -1420,20 +1593,25 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask); *flags &= ~IAudioFlinger::TRACK_FAST; - // For compatibility with AudioTrack calculation, buffer depth is forced - // to be at least 2 x the normal mixer frame count and cover audio hardware latency. - // This is probably too conservative, but legacy application code may depend on it. - // If you change this calculation, also review the start threshold which is related. + } + } + // For normal PCM streaming tracks, update minimum frame count. + // For compatibility with AudioTrack calculation, buffer depth is forced + // to be at least 2 x the normal mixer frame count and cover audio hardware latency. + // This is probably too conservative, but legacy application code may depend on it. + // If you change this calculation, also review the start threshold which is related. + if (!(*flags & IAudioFlinger::TRACK_FAST) + && audio_is_linear_pcm(format) && sharedBuffer == 0) { uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream); uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate); if (minBufCount < 2) { minBufCount = 2; } - size_t minFrameCount = mNormalFrameCount * minBufCount; - if (frameCount < minFrameCount) { + size_t minFrameCount = + minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate); + if (frameCount < minFrameCount) { // including frameCount == 0 frameCount = minFrameCount; } - } } *pFrameCount = frameCount; @@ -1831,7 +2009,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() LOG_FATAL("HAL format %#x not supported for mixed output", mFormat); } - mFrameSize = audio_stream_out_frame_size(mOutput->stream); + mFrameSize = mOutput->getFrameSize(); mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common); mFrameCount = mBufferSize / mFrameSize; if (mFrameCount & 15) { @@ -1861,6 +2039,22 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() } } + if (mType == DUPLICATING && mMixerBufferEnabled && mEffectBufferEnabled) { + // For best precision, we use float instead of the associated output + // device format (typically PCM 16 bit). + + mFormat = AUDIO_FORMAT_PCM_FLOAT; + mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); + mBufferSize = mFrameSize * mFrameCount; + + // TODO: We currently use the associated output device channel mask and sample rate. + // (1) Perhaps use the ORed channel mask of all downstream MixerThreads + // (if a valid mask) to avoid premature downmix. + // (2) Perhaps use the maximum sample rate of all downstream MixerThreads + // instead of the output device sample rate to avoid loss of high frequency information. + // This may need to be updated as MixerThread/OutputTracks are added and not here. + } + // Calculate size of normal sink buffer relative to the HAL output buffer size double multiplier = 1.0; if (mType == MIXER && (kUseFastMixer == FastMixer_Static || @@ -1966,7 +2160,7 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui } else { status_t status; uint32_t frames; - status = mOutput->stream->get_render_position(mOutput->stream, &frames); + status = mOutput->getRenderPosition(&frames); *dspFrames = (size_t)frames; return status; } @@ -2008,13 +2202,13 @@ uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId) } -AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::getOutput() const +AudioStreamOut* AudioFlinger::PlaybackThread::getOutput() const { Mutex::Autolock _l(mLock); return mOutput; } -AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::clearOutput() +AudioStreamOut* AudioFlinger::PlaybackThread::clearOutput() { Mutex::Autolock _l(mLock); AudioStreamOut *output = mOutput; @@ -2137,6 +2331,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() } else { bytesWritten = framesWritten; } + mLatchDValid = false; status_t status = mNormalSink->getTimestamp(mLatchD.mTimestamp); if (status == NO_ERROR) { size_t totalFramesWritten = mNormalSink->framesWritten(); @@ -2159,8 +2354,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() } // FIXME We should have an implementation of timestamps for direct output threads. // They are used e.g for multichannel PCM playback over HDMI. - bytesWritten = mOutput->stream->write(mOutput->stream, - (char *)mSinkBuffer + offset, mBytesRemaining); + bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining); if (mUseAsyncWrite && ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) { // do not wait for async callback in case of error of full write @@ -2640,7 +2834,9 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } else { + ATRACE_BEGIN("sleep"); usleep(sleepTime); + ATRACE_END(); } } @@ -2711,8 +2907,7 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) if ((mType == OFFLOAD || mType == DIRECT) && mOutput != NULL && mOutput->stream->get_presentation_position) { uint64_t position64; - int ret = mOutput->stream->get_presentation_position( - mOutput->stream, &position64, ×tamp.mTime); + int ret = mOutput->getPresentationPosition(&position64, ×tamp.mTime); if (ret == 0) { timestamp.mPosition = (uint32_t)position64; return NO_ERROR; @@ -2800,6 +2995,12 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mNormalFrameCount); mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); + if (type == DUPLICATING) { + // The Duplicating thread uses the AudioMixer and delivers data to OutputTracks + // (downstream MixerThreads) in DuplicatingThread::threadLoop_write(). + // Do not create or use mFastMixer, mOutputSink, mPipeSink, or mNormalSink. + return; + } // create an NBAIO sink for the HAL output stream, and negotiate mOutputSink = new AudioStreamOutSink(output->stream); size_t numCounterOffers = 0; @@ -2841,6 +3042,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud NBAIO_Format format = mOutputSink->format(); NBAIO_Format origformat = format; // adjust format to match that of the Fast Mixer + ALOGV("format changed from %d to %d", format.mFormat, fastMixerFormat); format.mFormat = fastMixerFormat; format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount; @@ -3020,8 +3222,10 @@ ssize_t AudioFlinger::MixerThread::threadLoop_write() #endif } state->mCommand = FastMixerState::MIX_WRITE; +#ifdef FAST_THREAD_STATISTICS mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? - FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); + FastThreadDumpState::kSamplingNforLowRamDevice : FastThreadDumpState::kSamplingN); +#endif sq->end(); sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED); if (kUseFastMixer == FastMixer_Dynamic) { @@ -3083,7 +3287,7 @@ bool AudioFlinger::PlaybackThread::waitingAsyncCallback() void AudioFlinger::PlaybackThread::threadLoop_standby() { ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended); - mOutput->stream->common.standby(&mOutput->stream->common); + mOutput->standby(); if (mUseAsyncWrite != 0) { // discard any pending drain or write ack by incrementing sequence mWriteAckSequence = (mWriteAckSequence + 2) & ~1; @@ -3386,8 +3590,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac if (sr == mSampleRate) { desiredFrames = mNormalFrameCount; } else { - // +1 for rounding and +1 for additional sample needed for interpolation - desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1; + desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate); // add frames already consumed but not yet released by the resampler // because mAudioTrackServerProxy->framesReady() will include these frames desiredFrames += mAudioMixer->getUnreleasedFrames(track->name()); @@ -3405,6 +3608,23 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } size_t framesReady = track->framesReady(); + if (ATRACE_ENABLED()) { + // I wish we had formatted trace names + char traceName[16]; + strcpy(traceName, "nRdy"); + int name = track->name(); + if (AudioMixer::TRACK0 <= name && + name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) { + name -= AudioMixer::TRACK0; + traceName[4] = (name / 10) + '0'; + traceName[5] = (name % 10) + '0'; + } else { + traceName[4] = '?'; + traceName[5] = '?'; + } + traceName[6] = '\0'; + ATRACE_INT(traceName, framesReady); + } if ((framesReady >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { @@ -3836,7 +4056,7 @@ bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePa status = mOutput->stream->common.set_parameters(&mOutput->stream->common, keyValuePair.string()); if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); + mOutput->standby(); mStandby = true; mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, @@ -4096,6 +4316,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } if (track->isStopping_1()) { track->mState = TrackBase::STOPPING_2; + if (last && mHwPaused) { + doHwResume = true; + mHwPaused = false; + } } if ((track->sharedBuffer() != 0) || track->isStopped() || track->isStopping_2() || track->isPaused()) { @@ -4178,8 +4402,8 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix() while (frameCount) { AudioBufferProvider::Buffer buffer; buffer.frameCount = frameCount; - mActiveTrack->getNextBuffer(&buffer); - if (buffer.raw == NULL) { + status_t status = mActiveTrack->getNextBuffer(&buffer); + if (status != NO_ERROR || buffer.raw == NULL) { memset(curBuf, 0, frameCount * mFrameSize); break; } @@ -4235,14 +4459,17 @@ void AudioFlinger::DirectOutputThread::threadLoop_exit() bool AudioFlinger::DirectOutputThread::shouldStandby_l() { bool trackPaused = false; + bool trackStopped = false; // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack // after a timeout and we will enter standby then. if (mTracks.size() > 0) { trackPaused = mTracks[mTracks.size() - 1]->isPaused(); + trackStopped = mTracks[mTracks.size() - 1]->isStopped() || + mTracks[mTracks.size() - 1]->mState == TrackBase::IDLE; } - return !mStandby && !(trackPaused || (usesHwAvSync() && mHwPaused)); + return !mStandby && !(trackPaused || (usesHwAvSync() && mHwPaused && !trackStopped)); } // getTrackName_l() must be called with ThreadBase::mLock held @@ -4291,7 +4518,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& key status = mOutput->stream->common.set_parameters(&mOutput->stream->common, keyValuePair.string()); if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); + mOutput->standby(); mStandby = true; mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, @@ -4345,7 +4572,10 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l() // use shorter standby delay as on normal output to release // hardware resources as soon as possible - if (audio_is_linear_pcm(mFormat)) { + // no delay on outputs with HW A/V sync + if (usesHwAvSync()) { + standbyDelay = 0; + } else if (audio_is_linear_pcm(mFormat)) { standbyDelay = microseconds(activeSleepTime*2); } else { standbyDelay = kOffloadStandbyDelayNs; @@ -4354,9 +4584,7 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l() void AudioFlinger::DirectOutputThread::flushHw_l() { - if (mOutput->stream->flush != NULL) { - mOutput->stream->flush(mOutput->stream); - } + mOutput->flush(); mHwPaused = false; } @@ -4646,7 +4874,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr size_t audioHALFrames = (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; size_t framesWritten = - mBytesWritten / audio_stream_out_frame_size(mOutput->stream); + mBytesWritten / mOutput->getFrameSize(); track->presentationComplete(framesWritten, audioHALFrames); track->reset(); tracksToRemove->add(track); @@ -4797,16 +5025,8 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() ssize_t AudioFlinger::DuplicatingThread::threadLoop_write() { - // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT - // for delivery downstream as needed. This in-place conversion is safe as - // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format - // (AUDIO_FORMAT_PCM_8_BIT is not allowed here). - if (mFormat != AUDIO_FORMAT_PCM_16_BIT) { - memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT, - mSinkBuffer, mFormat, writeFrames * mChannelCount); - } for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(reinterpret_cast<int16_t*>(mSinkBuffer), writeFrames); + outputTracks[i]->write(mSinkBuffer, writeFrames); } mStandby = false; return (ssize_t)mSinkBufferSize; @@ -4833,25 +5053,26 @@ void AudioFlinger::DuplicatingThread::clearOutputTracks() void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { Mutex::Autolock _l(mLock); - // FIXME explain this formula - size_t frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate(); - // OutputTrack is forced to AUDIO_FORMAT_PCM_16_BIT regardless of mFormat - // due to current usage case and restrictions on the AudioBufferProvider. - // Actual buffer conversion is done in threadLoop_write(). - // - // TODO: This may change in the future, depending on multichannel - // (and non int16_t*) support on AF::PlaybackThread::OutputTrack - OutputTrack *outputTrack = new OutputTrack(thread, + // The downstream MixerThread consumes thread->frameCount() amount of frames per mix pass. + // Adjust for thread->sampleRate() to determine minimum buffer frame count. + // Then triple buffer because Threads do not run synchronously and may not be clock locked. + const size_t frameCount = + 3 * sourceFramesNeeded(mSampleRate, thread->frameCount(), thread->sampleRate()); + // TODO: Consider asynchronous sample rate conversion to handle clock disparity + // from different OutputTracks and their associated MixerThreads (e.g. one may + // nearly empty and the other may be dropping data). + + sp<OutputTrack> outputTrack = new OutputTrack(thread, this, mSampleRate, - AUDIO_FORMAT_PCM_16_BIT, + mFormat, mChannelMask, frameCount, IPCThreadState::self()->getCallingUid()); if (outputTrack->cblk() != NULL) { thread->setStreamVolume(AUDIO_STREAM_PATCH, 1.0f); mOutputTracks.add(outputTrack); - ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); + ALOGV("addOutputTrack() track %p, on thread %p", outputTrack.get(), thread); updateWaitTime_l(); } } @@ -4952,8 +5173,8 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, // mFastCaptureNBLogWriter , mFastTrackAvail(false) { - snprintf(mName, kNameLength, "AudioIn_%X", id); - mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); + snprintf(mThreadName, kThreadNameLength, "AudioIn_%X", id); + mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName); readInputParameters_l(); @@ -4993,7 +5214,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, } if (initFastCapture) { - // create a Pipe for FastMixer to write to, and for us and fast tracks to read from + // create a Pipe for FastCapture to write to, and for us and fast tracks to read from NBAIO_Format format = mInputSource->format(); size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each size_t pipeSize = pipeFramesP2 * Format_frameSize(format); @@ -5094,7 +5315,7 @@ AudioFlinger::RecordThread::~RecordThread() void AudioFlinger::RecordThread::onFirstRef() { - run(mName, PRIORITY_URGENT_AUDIO); + run(mThreadName, PRIORITY_URGENT_AUDIO); } bool AudioFlinger::RecordThread::threadLoop() @@ -5135,7 +5356,9 @@ reacquire_wakelock: // sleep with mutex unlocked if (sleepUs > 0) { + ATRACE_BEGIN("sleep"); usleep(sleepUs); + ATRACE_END(); sleepUs = 0; } @@ -5279,7 +5502,8 @@ reacquire_wakelock: state->mCommand = FastCaptureState::READ_WRITE; #if 0 // FIXME mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? - FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); + FastThreadDumpState::kSamplingNforLowRamDevice : + FastThreadDumpState::kSamplingN); #endif didModify = true; } @@ -5427,8 +5651,8 @@ reacquire_wakelock: upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src, part1); } else { - downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, (const int16_t *)src, - part1); + downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, + (const int16_t *)src, part1); } dst += part1 * activeTrack->mFrameSize; front += part1; @@ -5649,8 +5873,9 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe // client expresses a preference for FAST, but we get the final say if (*flags & IAudioFlinger::TRACK_FAST) { if ( - // use case: callback handler - (tid != -1) && + // we formerly checked for a callback handler (non-0 tid), + // but that is no longer required for TRANSFER_OBTAIN mode + // // frame count is not specified, or is exactly the pipe depth ((frameCount == 0) || (frameCount == mPipeFramesP2)) && // PCM data @@ -5939,15 +6164,17 @@ void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& a { dprintf(fd, "\nInput thread %p:\n", this); - if (mActiveTracks.size() > 0) { - dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); - } else { + dumpBase(fd, args); + + if (mActiveTracks.size() == 0) { dprintf(fd, " No active record clients\n"); } dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no"); dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); - dumpBase(fd, args); + // Make a non-atomic copy of fast capture dump state so it won't change underneath us + const FastCaptureDumpState copy(mFastCaptureDumpState); + copy.dump(fd); } void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused) @@ -6412,4 +6639,4 @@ void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *co config->ext.mix.usecase.source = mAudioSource; } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 1088843..d600ea9 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -32,6 +32,8 @@ public: OFFLOAD // Thread class is OffloadThread }; + static const char *threadTypeToString(type_t type); + ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); @@ -406,6 +408,7 @@ protected: audio_channel_mask_t mChannelMask; uint32_t mChannelCount; size_t mFrameSize; + // not HAL frame size, this is for output sink (to pipe to fast mixer) audio_format_t mFormat; // Source format for Recording and // Sink format for Playback. // Sink format may be different than @@ -424,13 +427,13 @@ protected: bool mStandby; // Whether thread is currently in standby. audio_devices_t mOutDevice; // output device audio_devices_t mInDevice; // input device - audio_source_t mAudioSource; // (see audio.h, audio_source_t) + audio_source_t mAudioSource; const audio_io_handle_t mId; Vector< sp<EffectChain> > mEffectChains; - static const int kNameLength = 16; // prctl(PR_SET_NAME) limit - char mName[kNameLength]; + static const int kThreadNameLength = 16; // prctl(PR_SET_NAME) limit + char mThreadName[kThreadNameLength]; // guaranteed NUL-terminated sp<IPowerManager> mPowerManager; sp<IBinder> mWakeLockToken; const sp<PMDeathRecipient> mDeathRecipient; @@ -1167,7 +1170,8 @@ private: const sp<MemoryDealer> mReadOnlyHeap; // one-time initialization, no locks required - sp<FastCapture> mFastCapture; // non-0 if there is also a fast capture + sp<FastCapture> mFastCapture; // non-0 if there is also + // a fast capture // FIXME audio watchdog thread // contents are not guaranteed to be consistent, no locks required diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index e970036..dc9f249 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -20,6 +20,7 @@ //#define LOG_NDEBUG 0 #include "Configuration.h" +#include <linux/futex.h> #include <math.h> #include <sys/syscall.h> #include <utils/Log.h> @@ -404,9 +405,7 @@ AudioFlinger::PlaybackThread::Track::Track( mAudioTrackServerProxy(NULL), mResumeToStopping(false), mFlushHwPending(false), - mPreviousValid(false), - mPreviousFramesWritten(0) - // mPreviousTimestamp + mPreviousTimestampValid(false) { // client == 0 implies sharedBuffer == 0 ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); @@ -443,8 +442,6 @@ AudioFlinger::PlaybackThread::Track::Track( // this means we are potentially denying other more important fast tracks from // being created. It would be better to allocate the index dynamically. mFastIndex = i; - // Read the initial underruns because this field is never cleared by the fast mixer - mObservedUnderruns = thread->getFastTrackUnderruns(i); thread->mFastTrackAvailMask &= ~(1 << i); } } @@ -693,6 +690,12 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev } PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (isFastTrack()) { + // refresh fast track underruns on start because that field is never cleared + // by the fast mixer; furthermore, the same track can be recycled, i.e. start + // after stop. + mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex); + } status = playbackThread->addTrack_l(this); if (status == INVALID_OPERATION || status == PERMISSION_DENIED) { triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE); @@ -742,6 +745,7 @@ void AudioFlinger::PlaybackThread::Track::stop() // move to STOPPING_2 when drain completes and then STOPPED mState = STOPPING_1; } + playbackThread->broadcast_l(); ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName, playbackThread); } @@ -859,6 +863,7 @@ void AudioFlinger::PlaybackThread::Track::reset() if (mState == FLUSHED) { mState = IDLE; } + mPreviousTimestampValid = false; } } @@ -880,19 +885,22 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times { // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant if (isFastTrack()) { - // FIXME no lock held to set mPreviousValid = false + // FIXME no lock held to set mPreviousTimestampValid = false return INVALID_OPERATION; } sp<ThreadBase> thread = mThread.promote(); if (thread == 0) { - // FIXME no lock held to set mPreviousValid = false + // FIXME no lock held to set mPreviousTimestampValid = false return INVALID_OPERATION; } + Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + + status_t result = INVALID_OPERATION; if (!isOffloaded() && !isDirect()) { if (!playbackThread->mLatchQValid) { - mPreviousValid = false; + mPreviousTimestampValid = false; return INVALID_OPERATION; } uint32_t unpresentedFrames = @@ -908,36 +916,54 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times uint32_t framesWritten = i >= 0 ? playbackThread->mLatchQ.mFramesReleased[i] : mAudioTrackServerProxy->framesReleased(); - bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten; if (framesWritten < unpresentedFrames) { - mPreviousValid = false; - return INVALID_OPERATION; + mPreviousTimestampValid = false; + // return invalid result + } else { + timestamp.mPosition = framesWritten - unpresentedFrames; + timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime; + result = NO_ERROR; } - mPreviousFramesWritten = framesWritten; - uint32_t position = framesWritten - unpresentedFrames; - struct timespec time = playbackThread->mLatchQ.mTimestamp.mTime; - if (checkPreviousTimestamp) { - if (time.tv_sec < mPreviousTimestamp.mTime.tv_sec || - (time.tv_sec == mPreviousTimestamp.mTime.tv_sec && - time.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) { - ALOGW("Time is going backwards"); + } else { // offloaded or direct + result = playbackThread->getTimestamp_l(timestamp); + } + + // Prevent retrograde motion in timestamp. + if (result == NO_ERROR) { + if (mPreviousTimestampValid) { + if (timestamp.mTime.tv_sec < mPreviousTimestamp.mTime.tv_sec || + (timestamp.mTime.tv_sec == mPreviousTimestamp.mTime.tv_sec && + timestamp.mTime.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) { + ALOGW("WARNING - retrograde timestamp time"); + // FIXME Consider blocking this from propagating upwards. } + + // Looking at signed delta will work even when the timestamps + // are wrapping around. + int32_t deltaPosition = static_cast<int32_t>(timestamp.mPosition + - mPreviousTimestamp.mPosition); // position can bobble slightly as an artifact; this hides the bobble - static const uint32_t MINIMUM_POSITION_DELTA = 8u; - if ((position <= mPreviousTimestamp.mPosition) || - (position - mPreviousTimestamp.mPosition) < MINIMUM_POSITION_DELTA) { - position = mPreviousTimestamp.mPosition; - time = mPreviousTimestamp.mTime; + static const int32_t MINIMUM_POSITION_DELTA = 8; + if (deltaPosition < 0) { +#define TIME_TO_NANOS(time) ((uint64_t)time.tv_sec * 1000000000 + time.tv_nsec) + ALOGW("WARNING - retrograde timestamp position corrected," + " %d = %u - %u, (at %llu, %llu nanos)", + deltaPosition, + timestamp.mPosition, + mPreviousTimestamp.mPosition, + TIME_TO_NANOS(timestamp.mTime), + TIME_TO_NANOS(mPreviousTimestamp.mTime)); +#undef TIME_TO_NANOS + } + if (deltaPosition < MINIMUM_POSITION_DELTA) { + // Current timestamp is bad. Use last valid timestamp. + timestamp = mPreviousTimestamp; } } - timestamp.mPosition = position; - timestamp.mTime = time; mPreviousTimestamp = timestamp; - mPreviousValid = true; - return NO_ERROR; + mPreviousTimestampValid = true; } - - return playbackThread->getTimestamp_l(timestamp); + return result; } status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId) @@ -1709,36 +1735,18 @@ void AudioFlinger::PlaybackThread::OutputTrack::stop() mActive = false; } -bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames) +bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames) { Buffer *pInBuffer; Buffer inBuffer; - uint32_t channelCount = mChannelCount; bool outputBufferFull = false; inBuffer.frameCount = frames; - inBuffer.i16 = data; + inBuffer.raw = data; uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs(); if (!mActive && frames != 0) { - start(); - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - MixerThread *mixerThread = (MixerThread *)thread.get(); - if (mFrameCount > frames) { - if (mBufferQueue.size() < kMaxOverFlowBuffers) { - uint32_t startFrames = (mFrameCount - frames); - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[startFrames * channelCount]; - pInBuffer->frameCount = startFrames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - } else { - ALOGW("OutputTrack::write() %p no more buffers in queue", this); - } - } - } + (void) start(); } while (waitTimeLeftMs) { @@ -1773,20 +1781,20 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; - memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t)); + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * mFrameSize); Proxy::Buffer buf; buf.mFrameCount = outFrames; buf.mRaw = NULL; mClientProxy->releaseBuffer(&buf); pInBuffer->frameCount -= outFrames; - pInBuffer->i16 += outFrames * channelCount; + pInBuffer->raw = (int8_t *)pInBuffer->raw + outFrames * mFrameSize; mOutBuffer.frameCount -= outFrames; - mOutBuffer.i16 += outFrames * channelCount; + mOutBuffer.raw = (int8_t *)mOutBuffer.raw + outFrames * mFrameSize; if (pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { mBufferQueue.removeAt(0); - delete [] pInBuffer->mBuffer; + free(pInBuffer->mBuffer); delete pInBuffer; ALOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); @@ -1802,11 +1810,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr if (thread != 0 && !thread->standby()) { if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount]; + pInBuffer->mBuffer = malloc(inBuffer.frameCount * mFrameSize); pInBuffer->frameCount = inBuffer.frameCount; - pInBuffer->i16 = pInBuffer->mBuffer; - memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * - sizeof(int16_t)); + pInBuffer->raw = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * mFrameSize); mBufferQueue.add(pInBuffer); ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); @@ -1817,23 +1824,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr } } - // Calling write() with a 0 length buffer, means that no more data will be written: - // If no more buffers are pending, fill output track buffer to make sure it is started - // by output mixer. - if (frames == 0 && mBufferQueue.size() == 0) { - // FIXME borken, replace by getting framesReady() from proxy - size_t user = 0; // was mCblk->user - if (user < mFrameCount) { - frames = mFrameCount - user; - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[frames * channelCount]; - pInBuffer->frameCount = frames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - } else if (mActive) { - stop(); - } + // Calling write() with a 0 length buffer means that no more data will be written: + // We rely on stop() to set the appropriate flags to allow the remaining frames to play out. + if (frames == 0 && mBufferQueue.size() == 0 && mActive) { + stop(); } return outputBufferFull; @@ -1859,7 +1853,7 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() for (size_t i = 0; i < size; i++) { Buffer *pBuffer = mBufferQueue.itemAt(i); - delete [] pBuffer->mBuffer; + free(pBuffer->mBuffer); delete pBuffer; } mBufferQueue.clear(); @@ -2212,4 +2206,4 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe mProxy->releaseBuffer(buffer); } -}; // namespace android +} // namespace android diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp index 84a655a..7893778 100644 --- a/services/audioflinger/test-resample.cpp +++ b/services/audioflinger/test-resample.cpp @@ -427,6 +427,14 @@ int main(int argc, char* argv[]) { printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", quality, channels, time/1000000, output_frames * looplimit / (time / 1e9) / 1e6); resampler->reset(); + + // TODO fix legacy bug: reset does not clear buffers. + // delete and recreate resampler here. + delete resampler; + resampler = AudioResampler::create(format, channels, + output_freq, quality); + resampler->setSampleRate(input_freq); + resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT); } memset(output_vaddr, 0, output_size); diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh index 2c453b0..7f4d456 100755 --- a/services/audioflinger/tests/build_and_run_all_unit_tests.sh +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -15,7 +15,7 @@ mm echo "waiting for device" adb root && adb wait-for-device remount adb push $OUT/system/lib/libaudioresampler.so /system/lib -adb push $OUT/system/bin/resampler_tests /system/bin +adb push $OUT/data/nativetest/resampler_tests /system/bin sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh index 9b39e77..d0482a1 100755 --- a/services/audioflinger/tests/mixer_to_wav_tests.sh +++ b/services/audioflinger/tests/mixer_to_wav_tests.sh @@ -60,11 +60,21 @@ function createwav() { fi # Test: +# process__genericResampling with mixed integer and float track input +# track__Resample / track__genericResample + adb shell test-mixer $1 -s 48000 \ + -o /sdcard/tm48000grif.wav \ + sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 \ + sine:f,6,6000,19000 chirp:i,4,30000 + adb pull /sdcard/tm48000grif.wav $2 + +# Test: # process__genericResampling # track__Resample / track__genericResample adb shell test-mixer $1 -s 48000 \ -o /sdcard/tm48000gr.wav \ - sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 + sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 \ + sine:6,6000,19000 adb pull /sdcard/tm48000gr.wav $2 # Test: diff --git a/services/audioflinger/tests/test-mixer.cpp b/services/audioflinger/tests/test-mixer.cpp index 9a4fad6..8da6245 100644 --- a/services/audioflinger/tests/test-mixer.cpp +++ b/services/audioflinger/tests/test-mixer.cpp @@ -39,7 +39,7 @@ static void usage(const char* name) { fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]" " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]" " (<input-file> | <command>)+\n", name); - fprintf(stderr, " -f enable floating point input track\n"); + fprintf(stderr, " -f enable floating point input track by default\n"); fprintf(stderr, " -m enable floating point mixer output\n"); fprintf(stderr, " -c number of mixer output channels\n"); fprintf(stderr, " -s mixer sample-rate\n"); @@ -47,8 +47,8 @@ static void usage(const char* name) { fprintf(stderr, " -a <aux-buffer-file>\n"); fprintf(stderr, " -P # frames provided per call to resample() in CSV format\n"); fprintf(stderr, " <input-file> is a WAV file\n"); - fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n"); - fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n"); + fprintf(stderr, " <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n"); + fprintf(stderr, " 'chirp:[(i|f),]<channels>,<samplerate>'\n"); } static int writeFile(const char *filename, const void *buffer, @@ -78,6 +78,18 @@ static int writeFile(const char *filename, const void *buffer, return EXIT_SUCCESS; } +const char *parseFormat(const char *s, bool *useFloat) { + if (!strncmp(s, "f,", 2)) { + *useFloat = true; + return s + 2; + } + if (!strncmp(s, "i,", 2)) { + *useFloat = false; + return s + 2; + } + return s; +} + int main(int argc, char* argv[]) { const char* const progname = argv[0]; bool useInputFloat = false; @@ -88,8 +100,9 @@ int main(int argc, char* argv[]) { std::vector<int> Pvalues; const char* outputFilename = NULL; const char* auxFilename = NULL; - std::vector<int32_t> Names; - std::vector<SignalProvider> Providers; + std::vector<int32_t> names; + std::vector<SignalProvider> providers; + std::vector<audio_format_t> formats; for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) { switch (ch) { @@ -138,54 +151,65 @@ int main(int argc, char* argv[]) { size_t outputFrames = 0; // create providers for each track - Providers.resize(argc); + names.resize(argc); + providers.resize(argc); + formats.resize(argc); for (int i = 0; i < argc; ++i) { static const char chirp[] = "chirp:"; static const char sine[] = "sine:"; static const double kSeconds = 1; + bool useFloat = useInputFloat; if (!strncmp(argv[i], chirp, strlen(chirp))) { std::vector<int> v; + const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat); - parseCSV(argv[i] + strlen(chirp), v); + parseCSV(s, v); if (v.size() == 2) { printf("creating chirp(%d %d)\n", v[0], v[1]); - if (useInputFloat) { - Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); + if (useFloat) { + providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); + providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } else { fprintf(stderr, "malformed input '%s'\n", argv[i]); } } else if (!strncmp(argv[i], sine, strlen(sine))) { std::vector<int> v; + const char *s = parseFormat(argv[i] + strlen(sine), &useFloat); - parseCSV(argv[i] + strlen(sine), v); + parseCSV(s, v); if (v.size() == 3) { printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]); - if (useInputFloat) { - Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); + if (useFloat) { + providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); + providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } else { fprintf(stderr, "malformed input '%s'\n", argv[i]); } } else { printf("creating filename(%s)\n", argv[i]); if (useInputFloat) { - Providers[i].setFile<float>(argv[i]); + providers[i].setFile<float>(argv[i]); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setFile<short>(argv[i]); + providers[i].setFile<short>(argv[i]); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } // calculate the number of output frames - size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate - / Providers[i].getSampleRate(); + size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate + / providers[i].getSampleRate(); if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames outputFrames = nframes; } @@ -213,22 +237,20 @@ int main(int argc, char* argv[]) { // create the mixer. const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960 AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate); - audio_format_t inputFormat = useInputFloat - ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; audio_format_t mixerFormat = useMixerFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; - float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks + float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks static float f0; // zero // set up the tracks. - for (size_t i = 0; i < Providers.size(); ++i) { - //printf("track %d out of %d\n", i, Providers.size()); - uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels()); + for (size_t i = 0; i < providers.size(); ++i) { + //printf("track %d out of %d\n", i, providers.size()); + uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels()); int32_t name = mixer->getTrackName(channelMask, - inputFormat, AUDIO_SESSION_OUTPUT_MIX); + formats[i], AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); - Names.push_back(name); - mixer->setBufferProvider(name, &Providers[i]); + names[i] = name; + mixer->setBufferProvider(name, &providers[i]); mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)outputAddr); mixer->setParameter( @@ -240,7 +262,7 @@ int main(int argc, char* argv[]) { name, AudioMixer::TRACK, AudioMixer::FORMAT, - (void *)(uintptr_t)inputFormat); + (void *)(uintptr_t)formats[i]); mixer->setParameter( name, AudioMixer::TRACK, @@ -255,7 +277,7 @@ int main(int argc, char* argv[]) { name, AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, - (void *)(uintptr_t)Providers[i].getSampleRate()); + (void *)(uintptr_t)providers[i].getSampleRate()); if (useRamp) { mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0); mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0); @@ -277,11 +299,11 @@ int main(int argc, char* argv[]) { // pump the mixer to process data. size_t i; for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) { - for (size_t j = 0; j < Names.size(); ++j) { - mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + for (size_t j = 0; j < names.size(); ++j) { + mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (char *) outputAddr + i * outputFrameSize); if (auxFilename) { - mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, + mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, (char *) auxAddr + i * auxFrameSize); } } diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk index 188fc89..d4ce86a 100644 --- a/services/audiopolicy/Android.mk +++ b/services/audiopolicy/Android.mk @@ -3,25 +3,27 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyService.cpp \ - AudioPolicyEffects.cpp + service/AudioPolicyService.cpp \ + service/AudioPolicyEffects.cpp ifeq ($(USE_LEGACY_AUDIO_POLICY), 1) LOCAL_SRC_FILES += \ - AudioPolicyInterfaceImplLegacy.cpp \ - AudioPolicyClientImplLegacy.cpp + service/AudioPolicyInterfaceImplLegacy.cpp \ + service/AudioPolicyClientImplLegacy.cpp LOCAL_CFLAGS += -DUSE_LEGACY_AUDIO_POLICY else LOCAL_SRC_FILES += \ - AudioPolicyInterfaceImpl.cpp \ - AudioPolicyClientImpl.cpp + service/AudioPolicyInterfaceImpl.cpp \ + service/AudioPolicyClientImpl.cpp endif LOCAL_C_INCLUDES := \ $(TOPDIR)frameworks/av/services/audioflinger \ $(call include-path-for, audio-effects) \ - $(call include-path-for, audio-utils) + $(call include-path-for, audio-utils) \ + $(TOPDIR)frameworks/av/services/audiopolicy/common/include \ + $(TOPDIR)frameworks/av/services/audiopolicy/engine/interface \ LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -39,7 +41,8 @@ LOCAL_SHARED_LIBRARIES += \ endif LOCAL_STATIC_LIBRARIES := \ - libmedia_helper + libmedia_helper \ + libaudiopolicycomponents LOCAL_MODULE:= libaudiopolicyservice @@ -53,7 +56,7 @@ ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyManager.cpp + managerdefault/AudioPolicyManager.cpp \ LOCAL_SHARED_LIBRARIES := \ libcutils \ @@ -61,8 +64,15 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libsoundtrigger +LOCAL_SHARED_LIBRARIES += libaudiopolicyenginedefault + +LOCAL_C_INCLUDES += \ + $(TOPDIR)frameworks/av/services/audiopolicy/common/include \ + $(TOPDIR)frameworks/av/services/audiopolicy/engine/interface \ + LOCAL_STATIC_LIBRARIES := \ - libmedia_helper + libmedia_helper \ + libaudiopolicycomponents LOCAL_MODULE:= libaudiopolicymanagerdefault @@ -73,14 +83,26 @@ ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - AudioPolicyFactory.cpp + manager/AudioPolicyFactory.cpp LOCAL_SHARED_LIBRARIES := \ libaudiopolicymanagerdefault +LOCAL_STATIC_LIBRARIES := \ + libaudiopolicycomponents + +LOCAL_C_INCLUDES += \ + $(TOPDIR)frameworks/av/services/audiopolicy/common/include \ + $(TOPDIR)frameworks/av/services/audiopolicy/engine/interface \ + LOCAL_MODULE:= libaudiopolicymanager include $(BUILD_SHARED_LIBRARY) endif endif + +####################################################################### +# Recursive call sub-folder Android.mk +# +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h index 4508fa7..116d0d6 100644 --- a/services/audiopolicy/AudioPolicyInterface.h +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -75,7 +75,8 @@ public: // indicate a change in device connection status virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) = 0; + const char *device_address, + const char *device_name) = 0; // retrieve a device connection status virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address) = 0; diff --git a/services/audiopolicy/common/Android.mk b/services/audiopolicy/common/Android.mk new file mode 100644 index 0000000..dcce8e3 --- /dev/null +++ b/services/audiopolicy/common/Android.mk @@ -0,0 +1,9 @@ + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +####################################################################### +# Recursive call sub-folder Android.mk +# +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/services/audiopolicy/common/include/RoutingStrategy.h b/services/audiopolicy/common/include/RoutingStrategy.h new file mode 100644 index 0000000..d38967e --- /dev/null +++ b/services/audiopolicy/common/include/RoutingStrategy.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +namespace android { + +// Time in milliseconds after media stopped playing during which we consider that the +// sonification should be as unobtrusive as during the time media was playing. +#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 + +enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + STRATEGY_TRANSMITTED_THROUGH_SPEAKER, + STRATEGY_ACCESSIBILITY, + STRATEGY_REROUTING, + NUM_STRATEGIES +}; + +}; //namespace android diff --git a/services/audiopolicy/common/include/Volume.h b/services/audiopolicy/common/include/Volume.h new file mode 100755 index 0000000..a4cc759 --- /dev/null +++ b/services/audiopolicy/common/include/Volume.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <system/audio.h> +#include <utils/Log.h> + +class VolumeCurvePoint +{ +public: + int mIndex; + float mDBAttenuation; +}; + +class Volume +{ +public: + /** + * 4 points to define the volume attenuation curve, each characterized by the volume + * index (from 0 to 100) at which they apply, and the attenuation in dB at that index. + * we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl() + * + * @todo shall become configurable + */ + enum { + VOLMIN = 0, + VOLKNEE1 = 1, + VOLKNEE2 = 2, + VOLMAX = 3, + + VOLCNT = 4 + }; + + /** + * device categories used for volume curve management. + */ + enum device_category { + DEVICE_CATEGORY_HEADSET, + DEVICE_CATEGORY_SPEAKER, + DEVICE_CATEGORY_EARPIECE, + DEVICE_CATEGORY_EXT_MEDIA, + DEVICE_CATEGORY_CNT + }; + + /** + * extract one device relevant for volume control from multiple device selection + * + * @param[in] device for which the volume category is associated + * + * @return subset of device required to limit the number of volume category per device + */ + static audio_devices_t getDeviceForVolume(audio_devices_t device) + { + if (device == AUDIO_DEVICE_NONE) { + // this happens when forcing a route update and no track is active on an output. + // In this case the returned category is not important. + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (popcount(device) > 1) { + // Multiple device selection is either: + // - speaker + one other device: give priority to speaker in this case. + // - one A2DP device + another device: happens with duplicated output. In this case + // retain the device on the A2DP output as the other must not correspond to an active + // selection if not the speaker. + // - HDMI-CEC system audio mode only output: give priority to available item in order. + if (device & AUDIO_DEVICE_OUT_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { + device = AUDIO_DEVICE_OUT_HDMI_ARC; + } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { + device = AUDIO_DEVICE_OUT_AUX_LINE; + } else if (device & AUDIO_DEVICE_OUT_SPDIF) { + device = AUDIO_DEVICE_OUT_SPDIF; + } else { + device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); + } + } + + /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ + if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) + device = AUDIO_DEVICE_OUT_SPEAKER; + + ALOGW_IF(popcount(device) != 1, + "getDeviceForVolume() invalid device combination: %08x", + device); + + return device; + } + + /** + * returns the category the device belongs to with regard to volume curve management + * + * @param[in] device to check upon the category to whom it belongs to. + * + * @return device category. + */ + static device_category getDeviceCategory(audio_devices_t device) + { + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return DEVICE_CATEGORY_EARPIECE; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + return DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_LINE: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + /*USB? Remote submix?*/ + return DEVICE_CATEGORY_EXT_MEDIA; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: + default: + return DEVICE_CATEGORY_SPEAKER; + } + } + +}; diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h new file mode 100755 index 0000000..a2327ee --- /dev/null +++ b/services/audiopolicy/common/include/policy.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <system/audio.h> + +// For mixed output and inputs, the policy will use max mixer sampling rates. +// Do not limit sampling rate otherwise +#define MAX_MIXER_SAMPLING_RATE 48000 + +// For mixed output and inputs, the policy will use max mixer channel count. +// Do not limit channel count otherwise +#define MAX_MIXER_CHANNEL_COUNT 8 + +/** + * A device mask for all audio input devices that are considered "virtual" when evaluating + * active inputs in getActiveInput() + */ +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX|AUDIO_DEVICE_IN_FM_TUNER) + + +/** + * A device mask for all audio input and output devices where matching inputs/outputs on device + * type alone is not enough: the address must match too + */ +#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \ + AUDIO_DEVICE_OUT_REMOTE_SUBMIX) + +/** + * Check if the state given correspond to an in call state. + * @TODO find a better name for widely call state + * + * @param[in] state to consider + * + * @return true if given state represents a device in a telephony or VoIP call + */ +static inline bool is_state_in_call(int state) +{ + return (state == AUDIO_MODE_IN_CALL) || (state == AUDIO_MODE_IN_COMMUNICATION); +} + +/** + * Check if the input device given is considered as a virtual device. + * + * @param[in] device to consider + * + * @return true if the device is a virtual one, false otherwise. + */ +static bool is_virtual_input_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + +/** + * Check whether the device type is one + * where addresses are used to distinguish between one connected device and another + * + * @param[in] device to consider + * + * @return true if the device needs distinguish on address, false otherwise.. + */ +static bool device_distinguishes_on_address(audio_devices_t device) +{ + return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0); +} diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk new file mode 100644 index 0000000..71ba1cb --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + src/DeviceDescriptor.cpp \ + src/AudioGain.cpp \ + src/StreamDescriptor.cpp \ + src/HwModule.cpp \ + src/IOProfile.cpp \ + src/AudioPort.cpp \ + src/AudioPolicyMix.cpp \ + src/AudioPatch.cpp \ + src/AudioInputDescriptor.cpp \ + src/AudioOutputDescriptor.cpp \ + src/EffectDescriptor.cpp \ + src/ConfigParsingUtils.cpp \ + src/SoundTriggerSession.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/include \ + $(TOPDIR)frameworks/av/services/audiopolicy/common/include \ + +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include + +LOCAL_MODULE := libaudiopolicycomponents + +include $(BUILD_STATIC_LIBRARY) diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioGain.h b/services/audiopolicy/common/managerdefinitions/include/AudioGain.h new file mode 100644 index 0000000..21fbf9b --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioGain.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <system/audio.h> + +namespace android { + +class AudioGain: public RefBase +{ +public: + AudioGain(int index, bool useInChannelMask); + virtual ~AudioGain() {} + + void dump(int fd, int spaces, int index) const; + + void getDefaultConfig(struct audio_gain_config *config); + status_t checkConfig(const struct audio_gain_config *config); + int mIndex; + struct audio_gain mGain; + bool mUseInChannelMask; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h new file mode 100644 index 0000000..7536a37 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "AudioPort.h" +#include <utils/Errors.h> +#include <system/audio.h> +#include <utils/SortedVector.h> +#include <utils/KeyedVector.h> + +namespace android { + +class IOProfile; +class AudioMix; + +// descriptor for audio inputs. Used to maintain current configuration of each opened audio input +// and keep track of the usage of this input. +class AudioInputDescriptor: public AudioPortConfig +{ +public: + AudioInputDescriptor(const sp<IOProfile>& profile); + void setIoHandle(audio_io_handle_t ioHandle); + + audio_module_handle_t getModuleHandle() const; + + status_t dump(int fd); + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // input handle + audio_devices_t mDevice; // current device this input is routed to + AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount; // number of AudioRecord clients using + // this input + uint32_t mOpenRefCount; + audio_source_t mInputSource; // input source selected by application + //(mediarecorder.h) + const sp<IOProfile> mProfile; // I/O profile this output derives from + SortedVector<audio_session_t> mSessions; // audio sessions attached to this input + bool mIsSoundTrigger; // used by a soundtrigger capture + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; +}; + +class AudioInputCollection : + public DefaultKeyedVector< audio_io_handle_t, sp<AudioInputDescriptor> > +{ +public: + bool isSourceActive(audio_source_t source) const; + + sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const; + + uint32_t activeInputsCount() const; + + /** + * return io handle of active input or 0 if no input is active + * Only considers inputs from physical devices (e.g. main mic, headset mic) when + * ignoreVirtualInputs is true. + */ + audio_io_handle_t getActiveInput(bool ignoreVirtualInputs = true); + + audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; + + status_t dump(int fd) const; +}; + + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h new file mode 100644 index 0000000..43ee691 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "AudioPort.h" +#include <RoutingStrategy.h> +#include <utils/Errors.h> +#include <utils/Timers.h> +#include <utils/KeyedVector.h> +#include <system/audio.h> + +namespace android { + +class IOProfile; +class AudioMix; + +// descriptor for audio outputs. Used to maintain current configuration of each opened audio output +// and keep track of the usage of this output by each audio stream type. +class AudioOutputDescriptor: public AudioPortConfig +{ +public: + AudioOutputDescriptor(const sp<IOProfile>& profile); + + status_t dump(int fd); + + audio_devices_t device() const; + void changeRefCount(audio_stream_type_t stream, int delta); + + void setIoHandle(audio_io_handle_t ioHandle); + bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } + audio_devices_t supportedDevices(); + uint32_t latency(); + bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc); + bool isActive(uint32_t inPastMs = 0) const; + bool isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; + + audio_module_handle_t getModuleHandle() const; + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // output handle + uint32_t mLatency; // + audio_output_flags_t mFlags; // + audio_devices_t mDevice; // current device this output is routed to + AudioMix *mPolicyMix; // non NULL when used by a dynamic policy + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output + nsecs_t mStopTime[AUDIO_STREAM_CNT]; + sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output + sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output + float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume + int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter + const sp<IOProfile> mProfile; // I/O profile this output derives from + bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible + // device selection. See checkDeviceMuteStrategies() + uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) +}; + +class AudioOutputCollection : + public DefaultKeyedVector< audio_io_handle_t, sp<AudioOutputDescriptor> > +{ +public: + bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + + /** + * return whether a stream is playing remotely, override to change the definition of + * local/remote playback, used for instance by notification manager to not make + * media players lose audio focus when not playing locally + * For the base implementation, "remotely" means playing during screen mirroring which + * uses an output for playback with a non-empty, non "0" address. + */ + bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + + /** + * returns the A2DP output handle if it is open or 0 otherwise + */ + audio_io_handle_t getA2dpOutput() const; + + sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const; + + sp<AudioOutputDescriptor> getPrimaryOutput() const; + + /** + * return true if any output is playing anything besides the stream to ignore + */ + bool isAnyOutputActive(audio_stream_type_t streamToIgnore) const; + + audio_devices_t getSupportedDevices(audio_io_handle_t handle) const; + + status_t dump(int fd) const; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h b/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h new file mode 100644 index 0000000..385f257 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPatch.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <system/audio.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> + +namespace android { + +class AudioPatch : public RefBase +{ +public: + AudioPatch(const struct audio_patch *patch, uid_t uid); + + status_t dump(int fd, int spaces, int index) const; + + audio_patch_handle_t mHandle; + struct audio_patch mPatch; + uid_t mUid; + audio_patch_handle_t mAfPatchHandle; + +private: + static volatile int32_t mNextUniqueId; +}; + +class AudioPatchCollection : public DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > +{ +public: + status_t addAudioPatch(audio_patch_handle_t handle, const sp<AudioPatch>& patch); + + status_t removeAudioPatch(audio_patch_handle_t handle); + + status_t listAudioPatches(unsigned int *num_patches, struct audio_patch *patches) const; + + status_t dump(int fd) const; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h new file mode 100644 index 0000000..988aed6 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <utils/RefBase.h> +#include <media/AudioPolicy.h> +#include <utils/KeyedVector.h> +#include <hardware/audio.h> +#include <utils/String8.h> + +namespace android { + +class AudioOutputDescriptor; + +/** + * custom mix entry in mPolicyMixes + */ +class AudioPolicyMix : public RefBase { +public: + AudioPolicyMix() {} + + const sp<AudioOutputDescriptor> &getOutput() const; + + void setOutput(sp<AudioOutputDescriptor> &output); + + void clearOutput(); + + android::AudioMix &getMix(); + + void setMix(AudioMix &mix); + +private: + AudioMix mMix; // Audio policy mix descriptor + sp<AudioOutputDescriptor> mOutput; // Corresponding output stream +}; + + +class AudioPolicyMixCollection : public DefaultKeyedVector<String8, sp<AudioPolicyMix> > +{ +public: + status_t getAudioPolicyMix(String8 address, sp<AudioPolicyMix> &policyMix) const; + + status_t registerMix(String8 address, AudioMix mix); + + status_t unregisterMix(String8 address); + + void closeOutput(sp<AudioOutputDescriptor> &desc); + + /** + * Try to find an output descriptor for the given attributes. + * + * @param[in] attributes to consider for the research of output descriptor. + * @param[out] desc to return if an output could be found. + * + * @return NO_ERROR if an output was found for the given attribute (in this case, the + * descriptor output param is initialized), error code otherwise. + */ + status_t getOutputForAttr(audio_attributes_t attributes, sp<AudioOutputDescriptor> &desc); + + audio_devices_t getDeviceAndMixForInputSource(audio_source_t inputSource, + audio_devices_t availableDeviceTypes, + AudioMix **policyMix); + + status_t getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix); +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h new file mode 100644 index 0000000..4f7f2bc --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/RefBase.h> +#include <utils/Errors.h> +#include <system/audio.h> +#include <cutils/config_utils.h> + +namespace android { + +class HwModule; +class AudioGain; + +class AudioPort : public virtual RefBase +{ +public: + AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module); + virtual ~AudioPort() {} + + audio_port_handle_t getHandle() { return mId; } + + void attach(const sp<HwModule>& module); + bool isAttached() { return mId != 0; } + + static audio_port_handle_t getNextUniqueId(); + + virtual void toAudioPort(struct audio_port *port) const; + + void importAudioPort(const sp<AudioPort> port); + void clearCapabilities(); + + void loadSamplingRates(char *name); + void loadFormats(char *name); + void loadOutChannels(char *name); + void loadInChannels(char *name); + + audio_gain_mode_t loadGainMode(char *name); + void loadGain(cnode *root, int index); + virtual void loadGains(cnode *root); + + // searches for an exact match + status_t checkExactSamplingRate(uint32_t samplingRate) const; + // searches for a compatible match, and returns the best match via updatedSamplingRate + status_t checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const; + // searches for an exact match + status_t checkExactChannelMask(audio_channel_mask_t channelMask) const; + // searches for a compatible match, currently implemented for input channel masks only + status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const; + status_t checkFormat(audio_format_t format) const; + status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; + + uint32_t pickSamplingRate() const; + audio_channel_mask_t pickChannelMask() const; + audio_format_t pickFormat() const; + + static const audio_format_t sPcmFormatCompareTable[]; + static int compareFormats(audio_format_t format1, audio_format_t format2); + + audio_module_handle_t getModuleHandle() const; + + void dump(int fd, int spaces) const; + + String8 mName; + audio_port_type_t mType; + audio_port_role_t mRole; + bool mUseInChannelMask; + // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats + // indicates the supported parameters should be read from the output stream + // after it is opened for the first time + Vector <uint32_t> mSamplingRates; // supported sampling rates + Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks + Vector <audio_format_t> mFormats; // supported audio formats + Vector < sp<AudioGain> > mGains; // gain controllers + sp<HwModule> mModule; // audio HW module exposing this I/O stream + uint32_t mFlags; // attribute flags (e.g primary output, + // direct output...). + + +protected: + //TODO - clarify the role of mId in this case, both an "attached" indicator + // and a unique ID for identifying a port to the (upcoming) selection API, + // and its relationship to the mId in AudioOutputDescriptor and AudioInputDescriptor. + audio_port_handle_t mId; + +private: + static volatile int32_t mNextUniqueId; +}; + +class AudioPortConfig : public virtual RefBase +{ +public: + AudioPortConfig(); + virtual ~AudioPortConfig() {} + + status_t applyAudioPortConfig(const struct audio_port_config *config, + struct audio_port_config *backupConfig = NULL); + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const = 0; + virtual sp<AudioPort> getAudioPort() const = 0; + uint32_t mSamplingRate; + audio_format_t mFormat; + audio_channel_mask_t mChannelMask; + struct audio_gain_config mGain; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h new file mode 100644 index 0000000..53cb4a3 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "DeviceDescriptor.h" +#include "HwModule.h" +#include "audio_policy_conf.h" +#include <system/audio.h> +#include <utils/Log.h> +#include <utils/Vector.h> +#include <utils/SortedVector.h> +#include <cutils/config_utils.h> +#include <utils/RefBase.h> +#include <system/audio_policy.h> + +namespace android { + +// ---------------------------------------------------------------------------- +// Definitions for audio_policy.conf file parsing +// ---------------------------------------------------------------------------- + +struct StringToEnum { + const char *name; + uint32_t value; +}; + +#define STRING_TO_ENUM(string) { #string, string } +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +const StringToEnum sDeviceNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_TELEPHONY_TX), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_HDMI), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK), +}; + +const StringToEnum sOutputFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), +}; + +const StringToEnum sInputFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD), +}; + +const StringToEnum sFormatNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_FLOAT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED), + STRING_TO_ENUM(AUDIO_FORMAT_MP3), + STRING_TO_ENUM(AUDIO_FORMAT_AAC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_MAIN), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SSR), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LTP), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V1), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SCALABLE), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ERLC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LD), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V2), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ELD), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), + STRING_TO_ENUM(AUDIO_FORMAT_OPUS), + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), +}; + +const StringToEnum sOutChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), +}; + +const StringToEnum sInChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK), +}; + +const StringToEnum sGainModeNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT), + STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS), + STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP), +}; + +class ConfigParsingUtils +{ +public: + static uint32_t stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name); + static const char *enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value); + static bool stringToBool(const char *value); + static uint32_t parseOutputFlagNames(char *name); + static uint32_t parseInputFlagNames(char *name); + static audio_devices_t parseDeviceNames(char *name); + + static void loadHwModules(cnode *root, HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled); + + static void loadGlobalConfig(cnode *root, const sp<HwModule>& module, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled); + + static status_t loadAudioPolicyConfig(const char *path, + HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled); + +private: + static void loadHwModule(cnode *root, HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled); +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h new file mode 100644 index 0000000..d15f6b4 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "AudioPort.h" +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/SortedVector.h> +#include <cutils/config_utils.h> +#include <system/audio.h> +#include <system/audio_policy.h> + +namespace android { + +class DeviceDescriptor : public AudioPort, public AudioPortConfig +{ +public: + DeviceDescriptor(const String8& name, audio_devices_t type); + + virtual ~DeviceDescriptor() {} + + bool equals(const sp<DeviceDescriptor>& other) const; + + // AudioPortConfig + virtual sp<AudioPort> getAudioPort() const { return (AudioPort*) this; } + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + + // AudioPort + virtual void loadGains(cnode *root); + virtual void toAudioPort(struct audio_port *port) const; + + audio_devices_t type() const { return mDeviceType; } + status_t dump(int fd, int spaces, int index) const; + + String8 mAddress; + audio_port_handle_t mId; + + static String8 emptyNameStr; + +private: + audio_devices_t mDeviceType; + +friend class DeviceVector; +}; + +class DeviceVector : public SortedVector< sp<DeviceDescriptor> > +{ +public: + DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {} + + ssize_t add(const sp<DeviceDescriptor>& item); + ssize_t remove(const sp<DeviceDescriptor>& item); + ssize_t indexOf(const sp<DeviceDescriptor>& item) const; + + audio_devices_t types() const { return mDeviceTypes; } + + void loadDevicesFromType(audio_devices_t types); + void loadDevicesFromName(char *name, const DeviceVector& declaredDevices); + + sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const; + DeviceVector getDevicesFromType(audio_devices_t types) const; + sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const; + sp<DeviceDescriptor> getDeviceFromName(const String8& name) const; + DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) const; + + audio_devices_t getDevicesFromHwModule(audio_module_handle_t moduleHandle) const; + + audio_policy_dev_state_t getDeviceConnectionState(const sp<DeviceDescriptor> &devDesc) const; + + status_t dump(int fd, const String8 &direction) const; + +private: + void refreshTypes(); + audio_devices_t mDeviceTypes; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h new file mode 100644 index 0000000..c9783a1 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <RoutingStrategy.h> +#include <hardware/audio_effect.h> +#include <utils/KeyedVector.h> +#include <utils/RefBase.h> +#include <utils/Errors.h> + +namespace android { + + +class EffectDescriptor : public RefBase +{ +public: + status_t dump(int fd); + + int mIo; // io the effect is attached to + routing_strategy mStrategy; // routing strategy the effect is associated to + int mSession; // audio session the effect is on + effect_descriptor_t mDesc; // effect descriptor + bool mEnabled; // enabled state: CPU load being used or not +}; + +class EffectDescriptorCollection : public KeyedVector<int, sp<EffectDescriptor> > +{ +public: + EffectDescriptorCollection(); + + status_t registerEffect(const effect_descriptor_t *desc, audio_io_handle_t io, + uint32_t strategy, int session, int id); + status_t unregisterEffect(int id); + status_t setEffectEnabled(int id, bool enabled); + uint32_t getMaxEffectsCpuLoad() const; + uint32_t getMaxEffectsMemory() const; + bool isNonOffloadableEffectEnabled(); + + status_t dump(int fd); + +private: + status_t setEffectEnabled(const sp<EffectDescriptor> &effectDesc, bool enabled); + + uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects + uint32_t mTotalEffectsMemory; // current memory used by effects + + /** + * Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units + */ + static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000; + /** + * Maximum memory allocated to audio effects in KB + */ + static const uint32_t MAX_EFFECTS_MEMORY = 512; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h new file mode 100644 index 0000000..92c3ea2 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "DeviceDescriptor.h" +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/Errors.h> +#include <utils/Vector.h> +#include <system/audio.h> +#include <cutils/config_utils.h> + +namespace android { + +class IOProfile; + +class HwModule : public RefBase +{ +public: + HwModule(const char *name); + ~HwModule(); + + status_t loadOutput(cnode *root); + status_t loadInput(cnode *root); + status_t loadDevice(cnode *root); + + status_t addOutputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address); + status_t removeOutputProfile(String8 name); + status_t addInputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address); + status_t removeInputProfile(String8 name); + + audio_module_handle_t getHandle() const { return mHandle; } + + void dump(int fd); + + const char *const mName; // base name of the audio HW module (primary, a2dp ...) + uint32_t mHalVersion; // audio HAL API version + audio_module_handle_t mHandle; + Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module + Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module + DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf +}; + +class HwModuleCollection : public Vector< sp<HwModule> > +{ +public: + sp<HwModule> getModuleFromName(const char *name) const; + + sp <HwModule> getModuleForDevice(audio_devices_t device) const; + + sp<DeviceDescriptor> getDeviceDescriptor(const audio_devices_t device, + const char *device_address, + const char *device_name) const; + + status_t dump(int fd) const; +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/IOProfile.h b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h new file mode 100644 index 0000000..095e759 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/IOProfile.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "AudioPort.h" +#include "DeviceDescriptor.h" +#include <utils/String8.h> +#include <system/audio.h> + +namespace android { + +class HwModule; + +// the IOProfile class describes the capabilities of an output or input stream. +// It is currently assumed that all combination of listed parameters are supported. +// It is used by the policy manager to determine if an output or input is suitable for +// a given use case, open/close it accordingly and connect/disconnect audio tracks +// to/from it. +class IOProfile : public AudioPort +{ +public: + IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); + virtual ~IOProfile(); + + // This method is used for both output and input. + // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. + // For input, flags is interpreted as audio_input_flags_t. + // TODO: merge audio_output_flags_t and audio_input_flags_t. + bool isCompatibleProfile(audio_devices_t device, + String8 address, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + uint32_t flags) const; + + void dump(int fd); + void log(); + + DeviceVector mSupportedDevices; // supported devices + // (devices this output can be routed to) +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/SoundTriggerSession.h b/services/audiopolicy/common/managerdefinitions/include/SoundTriggerSession.h new file mode 100644 index 0000000..420e6d7 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/SoundTriggerSession.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <system/audio.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> + +namespace android { + +class SoundTriggerSessionCollection : public DefaultKeyedVector<audio_session_t, audio_io_handle_t> +{ +public: + status_t releaseSession(audio_session_t session); + + status_t acquireSession(audio_session_t session, audio_io_handle_t ioHandle); +}; + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h new file mode 100644 index 0000000..84db5ab --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/include/StreamDescriptor.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <Volume.h> +#include <utils/KeyedVector.h> +#include <utils/StrongPointer.h> +#include <utils/SortedVector.h> +#include <hardware/audio.h> + +namespace android { + +// stream descriptor used for volume control +class StreamDescriptor +{ +public: + StreamDescriptor(); + + int getVolumeIndex(audio_devices_t device) const; + bool canBeMuted() const { return mCanBeMuted; } + void clearCurrentVolumeIndex(); + void addCurrentVolumeIndex(audio_devices_t device, int index); + int getVolumeIndexMin() const { return mIndexMin; } + int getVolumeIndexMax() const { return mIndexMax; } + void setVolumeIndexMin(int volIndexMin); + void setVolumeIndexMax(int volIndexMax); + + void dump(int fd) const; + + void setVolumeCurvePoint(Volume::device_category deviceCategory, const VolumeCurvePoint *point); + const VolumeCurvePoint *getVolumeCurvePoint(Volume::device_category deviceCategory) const + { + return mVolumeCurve[deviceCategory]; + } + +private: + const VolumeCurvePoint *mVolumeCurve[Volume::DEVICE_CATEGORY_CNT]; + KeyedVector<audio_devices_t, int> mIndexCur; /**< current volume index per device. */ + int mIndexMin; /**< min volume index. */ + int mIndexMax; /**< max volume index. */ + bool mCanBeMuted; /**< true is the stream can be muted. */ +}; + +/** + * stream descriptors collection for volume control + */ +class StreamDescriptorCollection : public DefaultKeyedVector<audio_stream_type_t, StreamDescriptor> +{ +public: + StreamDescriptorCollection(); + + void clearCurrentVolumeIndex(audio_stream_type_t stream); + void addCurrentVolumeIndex(audio_stream_type_t stream, audio_devices_t device, int index); + + bool canBeMuted(audio_stream_type_t stream); + + status_t dump(int fd) const; + + void setVolumeCurvePoint(audio_stream_type_t stream, + Volume::device_category deviceCategory, + const VolumeCurvePoint *point); + + const VolumeCurvePoint *getVolumeCurvePoint(audio_stream_type_t stream, + Volume::device_category deviceCategory) const; + + void setVolumeIndexMin(audio_stream_type_t stream,int volIndexMin); + void setVolumeIndexMax(audio_stream_type_t stream,int volIndexMax); + +}; + +}; // namespace android diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/common/managerdefinitions/include/audio_policy_conf.h index 2535a67..a393e3b 100644 --- a/services/audiopolicy/audio_policy_conf.h +++ b/services/audiopolicy/common/managerdefinitions/include/audio_policy_conf.h @@ -14,9 +14,7 @@ * limitations under the License. */ - -#ifndef ANDROID_AUDIO_POLICY_CONF_H -#define ANDROID_AUDIO_POLICY_CONF_H +#pragma once ///////////////////////////////////////////////// @@ -53,9 +51,9 @@ // "formats" in outputs descriptors indicating that supported // values should be queried after opening the output. -#define DEVICES_TAG "devices" -#define DEVICE_TYPE "type" -#define DEVICE_ADDRESS "address" +#define APM_DEVICES_TAG "devices" +#define APM_DEVICE_TYPE "type" +#define APM_DEVICE_ADDRESS "address" #define MIXERS_TAG "mixers" #define MIXER_TYPE "type" @@ -71,7 +69,3 @@ #define GAIN_STEP_VALUE "step_value_mB" #define GAIN_MIN_RAMP_MS "min_ramp_ms" #define GAIN_MAX_RAMP_MS "max_ramp_ms" - - - -#endif // ANDROID_AUDIO_POLICY_CONF_H diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioGain.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioGain.cpp new file mode 100644 index 0000000..fc7b0cc --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioGain.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 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 "APM::AudioGain" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include "AudioGain.h" +#include "StreamDescriptor.h" +#include <utils/Log.h> +#include <utils/String8.h> + +#include <math.h> + +namespace android { + +AudioGain::AudioGain(int index, bool useInChannelMask) +{ + mIndex = index; + mUseInChannelMask = useInChannelMask; + memset(&mGain, 0, sizeof(struct audio_gain)); +} + +void AudioGain::getDefaultConfig(struct audio_gain_config *config) +{ + config->index = mIndex; + config->mode = mGain.mode; + config->channel_mask = mGain.channel_mask; + if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + config->values[0] = mGain.default_value; + } else { + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(mGain.channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(mGain.channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + config->values[i] = mGain.default_value; + } + } + if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + config->ramp_duration_ms = mGain.min_ramp_ms; + } +} + +status_t AudioGain::checkConfig(const struct audio_gain_config *config) +{ + if ((config->mode & ~mGain.mode) != 0) { + return BAD_VALUE; + } + if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + if ((config->values[0] < mGain.min_value) || + (config->values[0] > mGain.max_value)) { + return BAD_VALUE; + } + } else { + if ((config->channel_mask & ~mGain.channel_mask) != 0) { + return BAD_VALUE; + } + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(config->channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(config->channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + if ((config->values[i] < mGain.min_value) || + (config->values[i] > mGain.max_value)) { + return BAD_VALUE; + } + } + } + if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + if ((config->ramp_duration_ms < mGain.min_ramp_ms) || + (config->ramp_duration_ms > mGain.max_ramp_ms)) { + return BAD_VALUE; + } + } + return NO_ERROR; +} + +void AudioGain::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms); + result.append(buffer); + + write(fd, result.string(), result.size()); +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp new file mode 100644 index 0000000..fa66728 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 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 "APM::AudioInputDescriptor" +//#define LOG_NDEBUG 0 + +#include "AudioInputDescriptor.h" +#include "IOProfile.h" +#include "AudioGain.h" +#include "HwModule.h" +#include <media/AudioPolicy.h> +#include <policy.h> + +namespace android { + +AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), + mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0), + mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false) +{ + if (profile != NULL) { + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +void AudioInputDescriptor::setIoHandle(audio_io_handle_t ioHandle) +{ + mId = AudioPort::getNextUniqueId(); + mIoHandle = ioHandle; +} + +audio_module_handle_t AudioInputDescriptor::getModuleHandle() const +{ + return mProfile->getModuleHandle(); +} + +void AudioInputDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(mProfile != 0, + "toAudioPortConfig() called on input with null profile %d", mIoHandle); + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SINK; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; + dstConfig->ext.mix.handle = mIoHandle; + dstConfig->ext.mix.usecase.source = mInputSource; +} + +void AudioInputDescriptor::toAudioPort(struct audio_port *port) const +{ + ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle); + + mProfile->toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.mix.hw_module = mProfile->mModule->mHandle; + port->ext.mix.handle = mIoHandle; + port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL; +} + +status_t AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %d\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); + result.append(buffer); + snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +bool AudioInputCollection::isSourceActive(audio_source_t source) const +{ + for (size_t i = 0; i < size(); i++) { + const sp<AudioInputDescriptor> inputDescriptor = valueAt(i); + if (inputDescriptor->mRefCount == 0) { + continue; + } + if (inputDescriptor->mInputSource == (int)source) { + return true; + } + } + return false; +} + +sp<AudioInputDescriptor> AudioInputCollection::getInputFromId(audio_port_handle_t id) const +{ + sp<AudioInputDescriptor> inputDesc = NULL; + for (size_t i = 0; i < size(); i++) { + inputDesc = valueAt(i); + if (inputDesc->mId == id) { + break; + } + } + return inputDesc; +} + +uint32_t AudioInputCollection::activeInputsCount() const +{ + uint32_t count = 0; + for (size_t i = 0; i < size(); i++) { + const sp<AudioInputDescriptor> desc = valueAt(i); + if (desc->mRefCount > 0) { + count++; + } + } + return count; +} + +audio_io_handle_t AudioInputCollection::getActiveInput(bool ignoreVirtualInputs) +{ + for (size_t i = 0; i < size(); i++) { + const sp<AudioInputDescriptor> input_descriptor = valueAt(i); + if ((input_descriptor->mRefCount > 0) + && (!ignoreVirtualInputs || !is_virtual_input_device(input_descriptor->mDevice))) { + return keyAt(i); + } + } + return 0; +} + +audio_devices_t AudioInputCollection::getSupportedDevices(audio_io_handle_t handle) const +{ + sp<AudioInputDescriptor> inputDesc = valueFor(handle); + audio_devices_t devices = inputDesc->mProfile->mSupportedDevices.types(); + return devices; +} + +status_t AudioInputCollection::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\nInputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, "- Input %d dump:\n", keyAt(i)); + write(fd, buffer, strlen(buffer)); + valueAt(i)->dump(fd); + } + + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp new file mode 100644 index 0000000..cdb5b51 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2015 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 "APM::AudioOutputDescriptor" +//#define LOG_NDEBUG 0 + +#include "AudioOutputDescriptor.h" +#include "IOProfile.h" +#include "AudioGain.h" +#include "HwModule.h" +#include <media/AudioPolicy.h> + +// A device mask for all audio output devices that are considered "remote" when evaluating +// active output devices in isStreamActiveRemotely() +#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX + +namespace android { + +AudioOutputDescriptor::AudioOutputDescriptor(const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), mLatency(0), + mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), + mPatchHandle(0), + mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + mRefCount[i] = 0; + mCurVolume[i] = -1.0; + mMuteCount[i] = 0; + mStopTime[i] = 0; + } + for (int i = 0; i < NUM_STRATEGIES; i++) { + mStrategyMutedByDevice[i] = false; + } + if (profile != NULL) { + mFlags = (audio_output_flags_t)profile->mFlags; + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const +{ + return mProfile->getModuleHandle(); +} + +audio_devices_t AudioOutputDescriptor::device() const +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); + } else { + return mDevice; + } +} + +void AudioOutputDescriptor::setIoHandle(audio_io_handle_t ioHandle) +{ + mId = AudioPort::getNextUniqueId(); + mIoHandle = ioHandle; +} + +uint32_t AudioOutputDescriptor::latency() +{ + if (isDuplicated()) { + return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; + } else { + return mLatency; + } +} + +bool AudioOutputDescriptor::sharesHwModuleWith( + const sp<AudioOutputDescriptor> outputDesc) +{ + if (isDuplicated()) { + return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); + } else if (outputDesc->isDuplicated()){ + return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2); + } else { + return (mProfile->mModule == outputDesc->mProfile->mModule); + } +} + +void AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, + int delta) +{ + // forward usage count change to attached outputs + if (isDuplicated()) { + mOutput1->changeRefCount(stream, delta); + mOutput2->changeRefCount(stream, delta); + } + if ((delta + (int)mRefCount[stream]) < 0) { + ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", + delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +audio_devices_t AudioOutputDescriptor::supportedDevices() +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); + } else { + return mProfile->mSupportedDevices.types() ; + } +} + +bool AudioOutputDescriptor::isActive(uint32_t inPastMs) const +{ + nsecs_t sysTime = 0; + if (inPastMs != 0) { + sysTime = systemTime(); + } + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + if (i == AUDIO_STREAM_PATCH) { + continue; + } + if (isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if (mRefCount[stream] != 0) { + return true; + } + if (inPastMs == 0) { + return false; + } + if (sysTime == 0) { + sysTime = systemTime(); + } + if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { + return true; + } + return false; +} + +void AudioOutputDescriptor::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle); + + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; + dstConfig->ext.mix.handle = mIoHandle; + dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; +} + +void AudioOutputDescriptor::toAudioPort( + struct audio_port *port) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle); + mProfile->toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.mix.hw_module = mProfile->mModule->mHandle; + port->ext.mix.handle = mIoHandle; + port->ext.mix.latency_class = + mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL; +} + +status_t AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %08x\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Latency: %d\n", mLatency); + result.append(buffer); + snprintf(buffer, SIZE, " Flags %08x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", device()); + result.append(buffer); + snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); + result.append(buffer); + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", + i, mCurVolume[i], mRefCount[i], mMuteCount[i]); + result.append(buffer); + } + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +bool AudioOutputCollection::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < this->size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = this->valueAt(i); + if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioOutputCollection::isStreamActiveRemotely(audio_stream_type_t stream, + uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = valueAt(i); + if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && + outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + // do not consider re routing (when the output is going to a dynamic policy) + // as "remote playback" + if (outputDesc->mPolicyMix == NULL) { + return true; + } + } + } + return false; +} + +audio_io_handle_t AudioOutputCollection::getA2dpOutput() const +{ + for (size_t i = 0; i < size(); i++) { + sp<AudioOutputDescriptor> outputDesc = valueAt(i); + if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { + return this->keyAt(i); + } + } + return 0; +} + +sp<AudioOutputDescriptor> AudioOutputCollection::getPrimaryOutput() const +{ + for (size_t i = 0; i < size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = valueAt(i); + if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + return outputDesc; + } + } + return NULL; +} + +sp<AudioOutputDescriptor> AudioOutputCollection::getOutputFromId(audio_port_handle_t id) const +{ + sp<AudioOutputDescriptor> outputDesc = NULL; + for (size_t i = 0; i < size(); i++) { + outputDesc = valueAt(i); + if (outputDesc->mId == id) { + break; + } + } + return outputDesc; +} + +bool AudioOutputCollection::isAnyOutputActive(audio_stream_type_t streamToIgnore) const +{ + for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) { + if (s == (size_t) streamToIgnore) { + continue; + } + for (size_t i = 0; i < size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = valueAt(i); + if (outputDesc->mRefCount[s] != 0) { + return true; + } + } + } + return false; +} + +audio_devices_t AudioOutputCollection::getSupportedDevices(audio_io_handle_t handle) const +{ + sp<AudioOutputDescriptor> outputDesc = valueFor(handle); + audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types(); + return devices; +} + + +status_t AudioOutputCollection::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\nOutputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, "- Output %d dump:\n", keyAt(i)); + write(fd, buffer, strlen(buffer)); + valueAt(i)->dump(fd); + } + + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp new file mode 100644 index 0000000..3a317fa --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPatch.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 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 "APM::AudioPatch" +//#define LOG_NDEBUG 0 + +#include "AudioPatch.h" +#include "AudioGain.h" +#include "ConfigParsingUtils.h" +#include <cutils/log.h> +#include <utils/String8.h> + +namespace android { + +int32_t volatile AudioPatch::mNextUniqueId = 1; + +AudioPatch::AudioPatch(const struct audio_patch *patch, uid_t uid) : + mHandle(static_cast<audio_patch_handle_t>(android_atomic_inc(&mNextUniqueId))), + mPatch(*patch), + mUid(uid), + mAfPatchHandle(0) +{ +} + +status_t AudioPatch::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sources; i++) { + if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sources[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); + } + result.append(buffer); + } + snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sinks; i++) { + if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sinks[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); + } + result.append(buffer); + } + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPatchCollection::addAudioPatch(audio_patch_handle_t handle, + const sp<AudioPatch>& patch) +{ + ssize_t index = indexOfKey(handle); + + if (index >= 0) { + ALOGW("addAudioPatch() patch %d already in", handle); + return ALREADY_EXISTS; + } + add(handle, patch); + ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d" + "sink handle %d", + handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks, + patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id); + return NO_ERROR; +} + +status_t AudioPatchCollection::removeAudioPatch(audio_patch_handle_t handle) +{ + ssize_t index = indexOfKey(handle); + + if (index < 0) { + ALOGW("removeAudioPatch() patch %d not in", handle); + return ALREADY_EXISTS; + } + ALOGV("removeAudioPatch() handle %d af handle %d", handle, valueAt(index)->mAfPatchHandle); + removeItemsAt(index); + return NO_ERROR; +} + +status_t AudioPatchCollection::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches) const +{ + if (num_patches == NULL || (*num_patches != 0 && patches == NULL)) { + return BAD_VALUE; + } + ALOGV("listAudioPatches() num_patches %d patches %p available patches %zu", + *num_patches, patches, size()); + if (patches == NULL) { + *num_patches = 0; + } + + size_t patchesWritten = 0; + size_t patchesMax = *num_patches; + for (size_t i = 0; i < size() && patchesWritten < patchesMax; i++) { + const sp<AudioPatch> patch = valueAt(i); + patches[patchesWritten] = patch->mPatch; + patches[patchesWritten++].id = patch->mHandle; + ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d", + i, patch->mPatch.num_sources, patch->mPatch.num_sinks); + } + *num_patches = size(); + + ALOGV("listAudioPatches() got %zu patches needed %d", patchesWritten, *num_patches); + return NO_ERROR; +} + +status_t AudioPatchCollection::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "\nAudio Patches:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + valueAt(i)->dump(fd, 2, i); + } + return NO_ERROR; +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp new file mode 100644 index 0000000..84a53eb --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2015 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 "APM::AudioPolicyMix" +//#define LOG_NDEBUG 0 + +#include "AudioPolicyMix.h" +#include "HwModule.h" +#include "AudioPort.h" +#include "IOProfile.h" +#include "AudioGain.h" +#include <AudioOutputDescriptor.h> + +namespace android { + +void AudioPolicyMix::setOutput(sp<AudioOutputDescriptor> &output) +{ + mOutput = output; +} + +const sp<AudioOutputDescriptor> &AudioPolicyMix::getOutput() const +{ + return mOutput; +} + +void AudioPolicyMix::clearOutput() +{ + mOutput.clear(); +} + +void AudioPolicyMix::setMix(AudioMix &mix) +{ + mMix = mix; +} + +android::AudioMix &AudioPolicyMix::getMix() +{ + return mMix; +} + +status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix) +{ + ssize_t index = indexOfKey(address); + if (index >= 0) { + ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); + return BAD_VALUE; + } + sp<AudioPolicyMix> policyMix = new AudioPolicyMix(); + policyMix->setMix(mix); + add(address, policyMix); + return NO_ERROR; +} + +status_t AudioPolicyMixCollection::unregisterMix(String8 address) +{ + ssize_t index = indexOfKey(address); + if (index < 0) { + ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); + return BAD_VALUE; + } + + removeItemsAt(index); + return NO_ERROR; +} + +status_t AudioPolicyMixCollection::getAudioPolicyMix(String8 address, + sp<AudioPolicyMix> &policyMix) const +{ + ssize_t index = indexOfKey(address); + if (index < 0) { + ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); + return BAD_VALUE; + } + policyMix = valueAt(index); + return NO_ERROR; +} + +void AudioPolicyMixCollection::closeOutput(sp<AudioOutputDescriptor> &desc) +{ + for (size_t i = 0; i < size(); i++) { + sp<AudioPolicyMix> policyMix = valueAt(i); + if (policyMix->getOutput() == desc) { + policyMix->clearOutput(); + } + } +} + +status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, + sp<AudioOutputDescriptor> &desc) +{ + for (size_t i = 0; i < size(); i++) { + sp<AudioPolicyMix> policyMix = valueAt(i); + AudioMix mix = policyMix->getMix(); + + if (mix.mMixType == MIX_TYPE_PLAYERS) { + for (size_t j = 0; j < mix.mCriteria.size(); j++) { + if ((RULE_MATCH_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule && + mix.mCriteria[j].mAttr.mUsage == attributes.usage) || + (RULE_EXCLUDE_ATTRIBUTE_USAGE == mix.mCriteria[j].mRule && + mix.mCriteria[j].mAttr.mUsage != attributes.usage)) { + desc = policyMix->getOutput(); + break; + } + if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + strncmp(attributes.tags + strlen("addr="), + mix.mRegistrationId.string(), + AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { + desc = policyMix->getOutput(); + break; + } + } + } else if (mix.mMixType == MIX_TYPE_RECORDERS) { + if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE && + strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && + strncmp(attributes.tags + strlen("addr="), + mix.mRegistrationId.string(), + AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { + desc = policyMix->getOutput(); + } + } + if (desc != 0) { + desc->mPolicyMix = &mix; + return NO_ERROR; + } + } + return BAD_VALUE; +} + +audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource, + audio_devices_t availDevices, + AudioMix **policyMix) +{ + for (size_t i = 0; i < size(); i++) { + AudioMix mix = valueAt(i)->getMix(); + + if (mix.mMixType != MIX_TYPE_RECORDERS) { + continue; + } + for (size_t j = 0; j < mix.mCriteria.size(); j++) { + if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule && + mix.mCriteria[j].mAttr.mSource == inputSource) || + (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix.mCriteria[j].mRule && + mix.mCriteria[j].mAttr.mSource != inputSource)) { + if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + if (policyMix != NULL) { + *policyMix = &mix; + } + return AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + } + } + } + return AUDIO_DEVICE_NONE; +} + +status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix *&policyMix) +{ + if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) { + return BAD_VALUE; + } + String8 address(attr.tags + strlen("addr=")); + + ssize_t index = indexOfKey(address); + if (index < 0) { + ALOGW("getInputForAttr() no policy for address %s", address.string()); + return BAD_VALUE; + } + sp<AudioPolicyMix> audioPolicyMix = valueAt(index); + AudioMix mix = audioPolicyMix->getMix(); + + if (mix.mMixType != MIX_TYPE_PLAYERS) { + ALOGW("getInputForAttr() bad policy mix type for address %s", address.string()); + return BAD_VALUE; + } + policyMix = &mix; + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp new file mode 100644 index 0000000..46a119e --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp @@ -0,0 +1,804 @@ +/* + * Copyright (C) 2015 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 "APM::AudioPort" +//#define LOG_NDEBUG 0 + +#include "AudioPort.h" +#include "HwModule.h" +#include "AudioGain.h" +#include "ConfigParsingUtils.h" +#include "audio_policy_conf.h" +#include <policy.h> + +namespace android { + +int32_t volatile AudioPort::mNextUniqueId = 1; + +// --- AudioPort class implementation + +AudioPort::AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module) : + mName(name), mType(type), mRole(role), mModule(module), mFlags(0), mId(0) +{ + mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + +void AudioPort::attach(const sp<HwModule>& module) +{ + mId = getNextUniqueId(); + mModule = module; +} + +audio_port_handle_t AudioPort::getNextUniqueId() +{ + return static_cast<audio_port_handle_t>(android_atomic_inc(&mNextUniqueId)); +} + +audio_module_handle_t AudioPort::getModuleHandle() const +{ + return mModule->mHandle; +} + +void AudioPort::toAudioPort(struct audio_port *port) const +{ + port->role = mRole; + port->type = mType; + strlcpy(port->name, mName, AUDIO_PORT_MAX_NAME_LEN); + unsigned int i; + for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { + if (mSamplingRates[i] != 0) { + port->sample_rates[i] = mSamplingRates[i]; + } + } + port->num_sample_rates = i; + for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { + if (mChannelMasks[i] != 0) { + port->channel_masks[i] = mChannelMasks[i]; + } + } + port->num_channel_masks = i; + for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { + if (mFormats[i] != 0) { + port->formats[i] = mFormats[i]; + } + } + port->num_formats = i; + + ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); + + for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { + port->gains[i] = mGains[i]->mGain; + } + port->num_gains = i; +} + +void AudioPort::importAudioPort(const sp<AudioPort> port) { + for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { + const uint32_t rate = port->mSamplingRates.itemAt(k); + if (rate != 0) { // skip "dynamic" rates + bool hasRate = false; + for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { + if (rate == mSamplingRates.itemAt(l)) { + hasRate = true; + break; + } + } + if (!hasRate) { // never import a sampling rate twice + mSamplingRates.add(rate); + } + } + } + for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { + const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); + if (mask != 0) { // skip "dynamic" masks + bool hasMask = false; + for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { + if (mask == mChannelMasks.itemAt(l)) { + hasMask = true; + break; + } + } + if (!hasMask) { // never import a channel mask twice + mChannelMasks.add(mask); + } + } + } + for (size_t k = 0 ; k < port->mFormats.size() ; k++) { + const audio_format_t format = port->mFormats.itemAt(k); + if (format != 0) { // skip "dynamic" formats + bool hasFormat = false; + for (size_t l = 0 ; l < mFormats.size() ; l++) { + if (format == mFormats.itemAt(l)) { + hasFormat = true; + break; + } + } + if (!hasFormat) { // never import a channel mask twice + mFormats.add(format); + } + } + } + for (size_t k = 0 ; k < port->mGains.size() ; k++) { + sp<AudioGain> gain = port->mGains.itemAt(k); + if (gain != 0) { + bool hasGain = false; + for (size_t l = 0 ; l < mGains.size() ; l++) { + if (gain == mGains.itemAt(l)) { + hasGain = true; + break; + } + } + if (!hasGain) { // never import a gain twice + mGains.add(gain); + } + } + } +} + +void AudioPort::clearCapabilities() { + mChannelMasks.clear(); + mFormats.clear(); + mSamplingRates.clear(); + mGains.clear(); +} + +void AudioPort::loadSamplingRates(char *name) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling + // rates should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mSamplingRates.add(0); + return; + } + + while (str != NULL) { + uint32_t rate = atoi(str); + if (rate != 0) { + ALOGV("loadSamplingRates() adding rate %d", rate); + mSamplingRates.add(rate); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadFormats(char *name) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mFormats indicates the supported formats + // should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mFormats.add(AUDIO_FORMAT_DEFAULT); + return; + } + + while (str != NULL) { + audio_format_t format = (audio_format_t)ConfigParsingUtils::stringToEnum(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + str); + if (format != AUDIO_FORMAT_DEFAULT) { + mFormats.add(format); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadInChannels(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadInChannels() %s", name); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + str); + if (channelMask != 0) { + ALOGV("loadInChannels() adding channelMask %04x", channelMask); + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } +} + +void AudioPort::loadOutChannels(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadOutChannels() %s", name); + + // by convention, "0' in the first entry in mChannelMasks indicates the supported channel + // masks should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + str); + if (channelMask != 0) { + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + return; +} + +audio_gain_mode_t AudioPort::loadGainMode(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadGainMode() %s", name); + audio_gain_mode_t mode = 0; + while (str != NULL) { + mode |= (audio_gain_mode_t)ConfigParsingUtils::stringToEnum(sGainModeNameToEnumTable, + ARRAY_SIZE(sGainModeNameToEnumTable), + str); + str = strtok(NULL, "|"); + } + return mode; +} + +void AudioPort::loadGain(cnode *root, int index) +{ + cnode *node = root->first_child; + + sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); + + while (node) { + if (strcmp(node->name, GAIN_MODE) == 0) { + gain->mGain.mode = loadGainMode((char *)node->value); + } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { + if (mUseInChannelMask) { + gain->mGain.channel_mask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + (char *)node->value); + } else { + gain->mGain.channel_mask = + (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + (char *)node->value); + } + } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { + gain->mGain.min_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { + gain->mGain.max_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { + gain->mGain.default_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { + gain->mGain.step_value = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { + gain->mGain.min_ramp_ms = atoi((char *)node->value); + } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { + gain->mGain.max_ramp_ms = atoi((char *)node->value); + } + node = node->next; + } + + ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", + gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value); + + if (gain->mGain.mode == 0) { + return; + } + mGains.add(gain); +} + +void AudioPort::loadGains(cnode *root) +{ + cnode *node = root->first_child; + int index = 0; + while (node) { + ALOGV("loadGains() loading gain %s", node->name); + loadGain(node, index++); + node = node->next; + } +} + +status_t AudioPort::checkExactSamplingRate(uint32_t samplingRate) const +{ + if (mSamplingRates.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if (mSamplingRates[i] == samplingRate) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const +{ + if (mSamplingRates.isEmpty()) { + return NO_ERROR; + } + + // Search for the closest supported sampling rate that is above (preferred) + // or below (acceptable) the desired sampling rate, within a permitted ratio. + // The sampling rates do not need to be sorted in ascending order. + ssize_t maxBelow = -1; + ssize_t minAbove = -1; + uint32_t candidate; + for (size_t i = 0; i < mSamplingRates.size(); i++) { + candidate = mSamplingRates[i]; + if (candidate == samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + // candidate < desired + if (candidate < samplingRate) { + if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { + maxBelow = i; + } + // candidate > desired + } else { + if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { + minAbove = i; + } + } + } + // This uses hard-coded knowledge about AudioFlinger resampling ratios. + // TODO Move these assumptions out. + static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs + static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur + // due to approximation by an int32_t of the + // phase increments + // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. + if (minAbove >= 0) { + candidate = mSamplingRates[minAbove]; + if (candidate / kMaxDownSampleRatio <= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // But if we have to up-sample from a lower sampling rate, that's OK. + if (maxBelow >= 0) { + candidate = mSamplingRates[maxBelow]; + if (candidate * kMaxUpSampleRatio >= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // leave updatedSamplingRate unmodified + return BAD_VALUE; +} + +status_t AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const +{ + if (mChannelMasks.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mChannelMasks.size(); i++) { + if (mChannelMasks[i] == channelMask) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) + const +{ + if (mChannelMasks.isEmpty()) { + return NO_ERROR; + } + + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + // FIXME Does not handle multi-channel automatic conversions yet + audio_channel_mask_t supported = mChannelMasks[i]; + if (supported == channelMask) { + return NO_ERROR; + } + if (isRecordThread) { + // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. + // FIXME Abstract this out to a table. + if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) + && channelMask == AUDIO_CHANNEL_IN_MONO) || + (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK + || channelMask == AUDIO_CHANNEL_IN_STEREO))) { + return NO_ERROR; + } + } + } + return BAD_VALUE; +} + +status_t AudioPort::checkFormat(audio_format_t format) const +{ + if (mFormats.isEmpty()) { + return NO_ERROR; + } + + for (size_t i = 0; i < mFormats.size(); i ++) { + if (mFormats[i] == format) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + + +uint32_t AudioPort::pickSamplingRate() const +{ + // special case for uninitialized dynamic profile + if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { + return 0; + } + + // For direct outputs, pick minimum sampling rate: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t samplingRate = UINT_MAX; + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { + samplingRate = mSamplingRates[i]; + } + } + return (samplingRate == UINT_MAX) ? 0 : samplingRate; + } + + uint32_t samplingRate = 0; + uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; + + // For mixed output and inputs, use max mixer sampling rates. Do not + // limit sampling rate otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxRate = UINT_MAX; + } + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { + samplingRate = mSamplingRates[i]; + } + } + return samplingRate; +} + +audio_channel_mask_t AudioPort::pickChannelMask() const +{ + // special case for uninitialized dynamic profile + if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { + return AUDIO_CHANNEL_NONE; + } + audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE; + + // For direct outputs, pick minimum channel count: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t channelCount = UINT_MAX; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount < channelCount) && (cnlCount > 0)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; + } + + uint32_t channelCount = 0; + uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; + + // For mixed output and inputs, use max mixer channel count. Do not + // limit channel count otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxCount = UINT_MAX; + } + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; +} + +/* format in order of increasing preference */ +const audio_format_t AudioPort::sPcmFormatCompareTable[] = { + AUDIO_FORMAT_DEFAULT, + AUDIO_FORMAT_PCM_16_BIT, + AUDIO_FORMAT_PCM_8_24_BIT, + AUDIO_FORMAT_PCM_24_BIT_PACKED, + AUDIO_FORMAT_PCM_32_BIT, + AUDIO_FORMAT_PCM_FLOAT, +}; + +int AudioPort::compareFormats(audio_format_t format1, + audio_format_t format2) +{ + // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any + // compressed format and better than any PCM format. This is by design of pickFormat() + if (!audio_is_linear_pcm(format1)) { + if (!audio_is_linear_pcm(format2)) { + return 0; + } + return 1; + } + if (!audio_is_linear_pcm(format2)) { + return -1; + } + + int index1 = -1, index2 = -1; + for (size_t i = 0; + (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); + i ++) { + if (sPcmFormatCompareTable[i] == format1) { + index1 = i; + } + if (sPcmFormatCompareTable[i] == format2) { + index2 = i; + } + } + // format1 not found => index1 < 0 => format2 > format1 + // format2 not found => index2 < 0 => format2 < format1 + return index1 - index2; +} + +audio_format_t AudioPort::pickFormat() const +{ + // special case for uninitialized dynamic profile + if (mFormats.size() == 1 && mFormats[0] == 0) { + return AUDIO_FORMAT_DEFAULT; + } + + audio_format_t format = AUDIO_FORMAT_DEFAULT; + audio_format_t bestFormat = + AudioPort::sPcmFormatCompareTable[ + ARRAY_SIZE(AudioPort::sPcmFormatCompareTable) - 1]; + // For mixed output and inputs, use best mixer output format. Do not + // limit format otherwise + if ((mType != AUDIO_PORT_TYPE_MIX) || + ((mRole == AUDIO_PORT_ROLE_SOURCE) && + (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { + bestFormat = AUDIO_FORMAT_INVALID; + } + + for (size_t i = 0; i < mFormats.size(); i ++) { + if ((compareFormats(mFormats[i], format) > 0) && + (compareFormats(mFormats[i], bestFormat) <= 0)) { + format = mFormats[i]; + } + } + return format; +} + +status_t AudioPort::checkGain(const struct audio_gain_config *gainConfig, + int index) const +{ + if (index < 0 || (size_t)index >= mGains.size()) { + return BAD_VALUE; + } + return mGains[index]->checkConfig(gainConfig); +} + +void AudioPort::dump(int fd, int spaces) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + if (mName.size() != 0) { + snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); + result.append(buffer); + } + + if (mSamplingRates.size() != 0) { + snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + if (i == 0 && mSamplingRates[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "%d", mSamplingRates[i]); + } + result.append(buffer); + result.append(i == (mSamplingRates.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + + if (mChannelMasks.size() != 0) { + snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); + + if (i == 0 && mChannelMasks[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); + } + result.append(buffer); + result.append(i == (mChannelMasks.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + + if (mFormats.size() != 0) { + snprintf(buffer, SIZE, "%*s- formats: ", spaces, ""); + result.append(buffer); + for (size_t i = 0; i < mFormats.size(); i++) { + const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + mFormats[i]); + if (i == 0 && strcmp(formatStr, "") == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "%s", formatStr); + } + result.append(buffer); + result.append(i == (mFormats.size() - 1) ? "" : ", "); + } + result.append("\n"); + } + write(fd, result.string(), result.size()); + if (mGains.size() != 0) { + snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); + write(fd, buffer, strlen(buffer) + 1); + result.append(buffer); + for (size_t i = 0; i < mGains.size(); i++) { + mGains[i]->dump(fd, spaces + 2, i); + } + } +} + + +// --- AudioPortConfig class implementation + +AudioPortConfig::AudioPortConfig() +{ + mSamplingRate = 0; + mChannelMask = AUDIO_CHANNEL_NONE; + mFormat = AUDIO_FORMAT_INVALID; + mGain.index = -1; +} + +status_t AudioPortConfig::applyAudioPortConfig( + const struct audio_port_config *config, + struct audio_port_config *backupConfig) +{ + struct audio_port_config localBackupConfig; + status_t status = NO_ERROR; + + localBackupConfig.config_mask = config->config_mask; + toAudioPortConfig(&localBackupConfig); + + sp<AudioPort> audioport = getAudioPort(); + if (audioport == 0) { + status = NO_INIT; + goto exit; + } + if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + status = audioport->checkExactSamplingRate(config->sample_rate); + if (status != NO_ERROR) { + goto exit; + } + mSamplingRate = config->sample_rate; + } + if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + status = audioport->checkExactChannelMask(config->channel_mask); + if (status != NO_ERROR) { + goto exit; + } + mChannelMask = config->channel_mask; + } + if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + status = audioport->checkFormat(config->format); + if (status != NO_ERROR) { + goto exit; + } + mFormat = config->format; + } + if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { + status = audioport->checkGain(&config->gain, config->gain.index); + if (status != NO_ERROR) { + goto exit; + } + mGain = config->gain; + } + +exit: + if (status != NO_ERROR) { + applyAudioPortConfig(&localBackupConfig); + } + if (backupConfig != NULL) { + *backupConfig = localBackupConfig; + } + return status; +} + +void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + dstConfig->sample_rate = mSamplingRate; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { + dstConfig->sample_rate = srcConfig->sample_rate; + } + } else { + dstConfig->sample_rate = 0; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + dstConfig->channel_mask = mChannelMask; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { + dstConfig->channel_mask = srcConfig->channel_mask; + } + } else { + dstConfig->channel_mask = AUDIO_CHANNEL_NONE; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + dstConfig->format = mFormat; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { + dstConfig->format = srcConfig->format; + } + } else { + dstConfig->format = AUDIO_FORMAT_INVALID; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { + dstConfig->gain = mGain; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { + dstConfig->gain = srcConfig->gain; + } + } else { + dstConfig->gain.index = -1; + } + if (dstConfig->gain.index != -1) { + dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; + } else { + dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; + } +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp new file mode 100644 index 0000000..fe5bc5f --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/ConfigParsingUtils.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2015 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 "APM::ConfigParsingUtils" +//#define LOG_NDEBUG 0 + +#include "ConfigParsingUtils.h" +#include "AudioGain.h" +#include <hardware/audio.h> +#include <utils/Log.h> +#include <cutils/misc.h> + +namespace android { + +//static +uint32_t ConfigParsingUtils::stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name) +{ + for (size_t i = 0; i < size; i++) { + if (strcmp(table[i].name, name) == 0) { + ALOGV("stringToEnum() found %s", table[i].name); + return table[i].value; + } + } + return 0; +} + +//static +const char *ConfigParsingUtils::enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (table[i].value == value) { + return table[i].name; + } + } + return ""; +} + +//static +bool ConfigParsingUtils::stringToBool(const char *value) +{ + return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); +} + + +// --- audio_policy.conf file parsing +//static +uint32_t ConfigParsingUtils::parseOutputFlagNames(char *name) +{ + uint32_t flag = 0; + + // it is OK to cast name to non const here as we are not going to use it after + // strtok() modifies it + char *flagName = strtok(name, "|"); + while (flagName != NULL) { + if (strlen(flagName) != 0) { + flag |= ConfigParsingUtils::stringToEnum(sOutputFlagNameToEnumTable, + ARRAY_SIZE(sOutputFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flag |= AUDIO_OUTPUT_FLAG_DIRECT; + } + + return flag; +} + +//static +uint32_t ConfigParsingUtils::parseInputFlagNames(char *name) +{ + uint32_t flag = 0; + + // it is OK to cast name to non const here as we are not going to use it after + // strtok() modifies it + char *flagName = strtok(name, "|"); + while (flagName != NULL) { + if (strlen(flagName) != 0) { + flag |= stringToEnum(sInputFlagNameToEnumTable, + ARRAY_SIZE(sInputFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + return flag; +} + +//static +audio_devices_t ConfigParsingUtils::parseDeviceNames(char *name) +{ + uint32_t device = 0; + + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + device |= stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + } + devName = strtok(NULL, "|"); + } + return device; +} + +//static +void ConfigParsingUtils::loadHwModule(cnode *root, HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnable) +{ + status_t status = NAME_NOT_FOUND; + cnode *node; + sp<HwModule> module = new HwModule(root->name); + + node = config_find(root, DEVICES_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading device %s", node->name); + status_t tmpStatus = module->loadDevice(node); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + node = config_find(root, OUTPUTS_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading output %s", node->name); + status_t tmpStatus = module->loadOutput(node); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + node = config_find(root, INPUTS_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading input %s", node->name); + status_t tmpStatus = module->loadInput(node); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + loadGlobalConfig(root, module, availableInputDevices, availableOutputDevices, + defaultOutputDevices, isSpeakerDrcEnable); + + if (status == NO_ERROR) { + hwModules.add(module); + } +} + +//static +void ConfigParsingUtils::loadHwModules(cnode *root, HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled) +{ + cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); + if (node == NULL) { + return; + } + + node = node->first_child; + while (node) { + ALOGV("loadHwModules() loading module %s", node->name); + loadHwModule(node, hwModules, availableInputDevices, availableOutputDevices, + defaultOutputDevices, isSpeakerDrcEnabled); + node = node->next; + } +} + +//static +void ConfigParsingUtils::loadGlobalConfig(cnode *root, const sp<HwModule>& module, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevice, + bool &speakerDrcEnabled) +{ + cnode *node = config_find(root, GLOBAL_CONFIG_TAG); + + if (node == NULL) { + return; + } + DeviceVector declaredDevices; + if (module != NULL) { + declaredDevices = module->mDeclaredDevices; + } + + node = node->first_child; + while (node) { + if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { + availableOutputDevices.loadDevicesFromName((char *)node->value, + declaredDevices); + ALOGV("loadGlobalConfig() Attached Output Devices %08x", + availableOutputDevices.types()); + } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { + audio_devices_t device = (audio_devices_t)stringToEnum( + sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + (char *)node->value); + if (device != AUDIO_DEVICE_NONE) { + defaultOutputDevice = new DeviceDescriptor(String8("default-output"), device); + } else { + ALOGW("loadGlobalConfig() default device not specified"); + } + ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", defaultOutputDevice->type()); + } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { + availableInputDevices.loadDevicesFromName((char *)node->value, + declaredDevices); + ALOGV("loadGlobalConfig() Available InputDevices %08x", availableInputDevices.types()); + } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { + speakerDrcEnabled = stringToBool((char *)node->value); + ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", speakerDrcEnabled); + } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { + uint32_t major, minor; + sscanf((char *)node->value, "%u.%u", &major, &minor); + module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor); + ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u", + module->mHalVersion, major, minor); + } + node = node->next; + } +} + +//static +status_t ConfigParsingUtils::loadAudioPolicyConfig(const char *path, + HwModuleCollection &hwModules, + DeviceVector &availableInputDevices, + DeviceVector &availableOutputDevices, + sp<DeviceDescriptor> &defaultOutputDevices, + bool &isSpeakerDrcEnabled) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + loadHwModules(root, hwModules, + availableInputDevices, availableOutputDevices, + defaultOutputDevices, isSpeakerDrcEnabled); + // legacy audio_policy.conf files have one global_configuration section + loadGlobalConfig(root, hwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY), + availableInputDevices, availableOutputDevices, + defaultOutputDevices, isSpeakerDrcEnabled); + config_free(root); + free(root); + free(data); + + ALOGI("loadAudioPolicyConfig() loaded %s\n", path); + + return NO_ERROR; +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp new file mode 100644 index 0000000..7df7d75 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2015 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 "APM::Devices" +//#define LOG_NDEBUG 0 + +#include "DeviceDescriptor.h" +#include "AudioGain.h" +#include "HwModule.h" +#include "ConfigParsingUtils.h" + +namespace android { + +String8 DeviceDescriptor::emptyNameStr = String8(""); + +DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : + AudioPort(name, AUDIO_PORT_TYPE_DEVICE, + audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : + AUDIO_PORT_ROLE_SOURCE, + NULL), + mAddress(""), mDeviceType(type) +{ + +} + +bool DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const +{ + // Devices are considered equal if they: + // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE) + // - have the same address or one device does not specify the address + // - have the same channel mask or one device does not specify the channel mask + return (mDeviceType == other->mDeviceType) && + (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && + (mChannelMask == 0 || other->mChannelMask == 0 || + mChannelMask == other->mChannelMask); +} + +void DeviceDescriptor::loadGains(cnode *root) +{ + AudioPort::loadGains(root); + if (mGains.size() > 0) { + mGains[0]->getDefaultConfig(&mGain); + } +} + +void DeviceVector::refreshTypes() +{ + mDeviceTypes = AUDIO_DEVICE_NONE; + for(size_t i = 0; i < size(); i++) { + mDeviceTypes |= itemAt(i)->type(); + } + ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes); +} + +ssize_t DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const +{ + for(size_t i = 0; i < size(); i++) { + if (item->equals(itemAt(i))) { + return i; + } + } + return -1; +} + +ssize_t DeviceVector::add(const sp<DeviceDescriptor>& item) +{ + ssize_t ret = indexOf(item); + + if (ret < 0) { + ret = SortedVector::add(item); + if (ret >= 0) { + refreshTypes(); + } + } else { + ALOGW("DeviceVector::add device %08x already in", item->type()); + ret = -1; + } + return ret; +} + +ssize_t DeviceVector::remove(const sp<DeviceDescriptor>& item) +{ + size_t i; + ssize_t ret = indexOf(item); + + if (ret < 0) { + ALOGW("DeviceVector::remove device %08x not in", item->type()); + } else { + ret = SortedVector::removeAt(ret); + if (ret >= 0) { + refreshTypes(); + } + } + return ret; +} + +audio_devices_t DeviceVector::getDevicesFromHwModule(audio_module_handle_t moduleHandle) const +{ + audio_devices_t devices = AUDIO_DEVICE_NONE; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->getModuleHandle() == moduleHandle) { + devices |= itemAt(i)->type(); + } + } + return devices; +} + +void DeviceVector::loadDevicesFromType(audio_devices_t types) +{ + DeviceVector deviceList; + + uint32_t role_bit = AUDIO_DEVICE_BIT_IN & types; + types &= ~role_bit; + + while (types) { + uint32_t i = 31 - __builtin_clz(types); + uint32_t type = 1 << i; + types &= ~type; + add(new DeviceDescriptor(String8("device_type"), type | role_bit)); + } +} + +void DeviceVector::loadDevicesFromName(char *name, + const DeviceVector& declaredDevices) +{ + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + audio_devices_t type = ConfigParsingUtils::stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + if (type != AUDIO_DEVICE_NONE) { + sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(name), type); + if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || + type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { + dev->mAddress = String8("0"); + } + add(dev); + } else { + sp<DeviceDescriptor> deviceDesc = + declaredDevices.getDeviceFromName(String8(devName)); + if (deviceDesc != 0) { + add(deviceDesc); + } + } + } + devName = strtok(NULL, "|"); + } +} + +sp<DeviceDescriptor> DeviceVector::getDevice(audio_devices_t type, String8 address) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->type() == type) { + if (address == "" || itemAt(i)->mAddress == address) { + device = itemAt(i); + if (itemAt(i)->mAddress == address) { + break; + } + } + } + } + ALOGV("DeviceVector::getDevice() for type %08x address %s found %p", + type, address.string(), device.get()); + return device; +} + +sp<DeviceDescriptor> DeviceVector::getDeviceFromId(audio_port_handle_t id) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->getHandle() == id) { + device = itemAt(i); + break; + } + } + return device; +} + +DeviceVector DeviceVector::getDevicesFromType(audio_devices_t type) const +{ + DeviceVector devices; + bool isOutput = audio_is_output_devices(type); + type &= ~AUDIO_DEVICE_BIT_IN; + for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { + bool curIsOutput = audio_is_output_devices(itemAt(i)->mDeviceType); + audio_devices_t curType = itemAt(i)->mDeviceType & ~AUDIO_DEVICE_BIT_IN; + if ((isOutput == curIsOutput) && ((type & curType) != 0)) { + devices.add(itemAt(i)); + type &= ~curType; + ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", + itemAt(i)->type(), itemAt(i).get()); + } + } + return devices; +} + +DeviceVector DeviceVector::getDevicesFromTypeAddr( + audio_devices_t type, String8 address) const +{ + DeviceVector devices; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->type() == type) { + if (itemAt(i)->mAddress == address) { + devices.add(itemAt(i)); + } + } + } + return devices; +} + +sp<DeviceDescriptor> DeviceVector::getDeviceFromName(const String8& name) const +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mName == name) { + device = itemAt(i); + break; + } + } + return device; +} + + +status_t DeviceVector::dump(int fd, const String8 &direction) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\n Available %s devices:\n", direction.string()); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + itemAt(i)->dump(fd, 2, i); + } + return NO_ERROR; +} + +audio_policy_dev_state_t DeviceVector::getDeviceConnectionState(const sp<DeviceDescriptor> &devDesc) const +{ + ssize_t index = indexOf(devDesc); + return index >= 0 ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; +} + +void DeviceDescriptor::toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = audio_is_output_device(mDeviceType) ? + AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_DEVICE; + dstConfig->ext.device.type = mDeviceType; + + //TODO Understand why this test is necessary. i.e. why at boot time does it crash + // without the test? + // This has been demonstrated to NOT be true (at start up) + // ALOG_ASSERT(mModule != NULL); + dstConfig->ext.device.hw_module = mModule != 0 ? mModule->mHandle : AUDIO_IO_HANDLE_NONE; + strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); +} + +void DeviceDescriptor::toAudioPort(struct audio_port *port) const +{ + ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); + AudioPort::toAudioPort(port); + port->id = mId; + toAudioPortConfig(&port->active_config); + port->ext.device.type = mDeviceType; + port->ext.device.hw_module = mModule->mHandle; + strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); +} + +status_t DeviceDescriptor::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1); + result.append(buffer); + if (mId != 0) { + snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId); + result.append(buffer); + } + snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "", + ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mDeviceType)); + result.append(buffer); + if (mAddress.size() != 0) { + snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string()); + result.append(buffer); + } + write(fd, result.string(), result.size()); + AudioPort::dump(fd, spaces); + + return NO_ERROR; +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp new file mode 100644 index 0000000..33d838d --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 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 "APM::EffectDescriptor" +//#define LOG_NDEBUG 0 + +#include "EffectDescriptor.h" +#include <utils/String8.h> + +namespace android { + +status_t EffectDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " I/O: %d\n", mIo); + result.append(buffer); + snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); + result.append(buffer); + snprintf(buffer, SIZE, " Session: %d\n", mSession); + result.append(buffer); + snprintf(buffer, SIZE, " Name: %s\n", mDesc.name); + result.append(buffer); + snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled"); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +EffectDescriptorCollection::EffectDescriptorCollection() : + mTotalEffectsCpuLoad(0), + mTotalEffectsMemory(0) +{ + +} + +status_t EffectDescriptorCollection::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { + ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", + desc->name, desc->memoryUsage); + return INVALID_OPERATION; + } + mTotalEffectsMemory += desc->memoryUsage; + ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", + desc->name, io, strategy, session, id); + ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); + + sp<EffectDescriptor> effectDesc = new EffectDescriptor(); + memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); + effectDesc->mIo = io; + effectDesc->mStrategy = static_cast<routing_strategy>(strategy); + effectDesc->mSession = session; + effectDesc->mEnabled = false; + + add(id, effectDesc); + + return NO_ERROR; +} + +status_t EffectDescriptorCollection::unregisterEffect(int id) +{ + ssize_t index = indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + sp<EffectDescriptor> effectDesc = valueAt(index); + + setEffectEnabled(effectDesc, false); + + if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) { + ALOGW("unregisterEffect() memory %d too big for total %d", + effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); + effectDesc->mDesc.memoryUsage = mTotalEffectsMemory; + } + mTotalEffectsMemory -= effectDesc->mDesc.memoryUsage; + ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", + effectDesc->mDesc.name, id, effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); + + removeItem(id); + + return NO_ERROR; +} + +status_t EffectDescriptorCollection::setEffectEnabled(int id, bool enabled) +{ + ssize_t index = indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + return setEffectEnabled(valueAt(index), enabled); +} + + +status_t EffectDescriptorCollection::setEffectEnabled(const sp<EffectDescriptor> &effectDesc, + bool enabled) +{ + if (enabled == effectDesc->mEnabled) { + ALOGV("setEffectEnabled(%s) effect already %s", + enabled?"true":"false", enabled?"enabled":"disabled"); + return INVALID_OPERATION; + } + + if (enabled) { + if (mTotalEffectsCpuLoad + effectDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { + ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", + effectDesc->mDesc.name, (float)effectDesc->mDesc.cpuLoad/10); + return INVALID_OPERATION; + } + mTotalEffectsCpuLoad += effectDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); + } else { + if (mTotalEffectsCpuLoad < effectDesc->mDesc.cpuLoad) { + ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", + effectDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); + effectDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; + } + mTotalEffectsCpuLoad -= effectDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); + } + effectDesc->mEnabled = enabled; + return NO_ERROR; +} + +bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() +{ + for (size_t i = 0; i < size(); i++) { + sp<EffectDescriptor> effectDesc = valueAt(i); + if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) && + ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { + ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", + effectDesc->mDesc.name, effectDesc->mSession); + return true; + } + } + return false; +} + +uint32_t EffectDescriptorCollection::getMaxEffectsCpuLoad() const +{ + return MAX_EFFECTS_CPU_LOAD; +} + +uint32_t EffectDescriptorCollection::getMaxEffectsMemory() const +{ + return MAX_EFFECTS_MEMORY; +} + +status_t EffectDescriptorCollection::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", + (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); + write(fd, buffer, strlen(buffer)); + + snprintf(buffer, SIZE, "Registered effects:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, "- Effect %d dump:\n", keyAt(i)); + write(fd, buffer, strlen(buffer)); + valueAt(i)->dump(fd); + } + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp new file mode 100644 index 0000000..0097d69 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2015 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 "APM::HwModule" +//#define LOG_NDEBUG 0 + +#include "HwModule.h" +#include "IOProfile.h" +#include "AudioGain.h" +#include "ConfigParsingUtils.h" +#include "audio_policy_conf.h" +#include <hardware/audio.h> +#include <policy.h> + +namespace android { + +HwModule::HwModule(const char *name) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), + mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) +{ +} + +HwModule::~HwModule() +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + mOutputProfiles[i]->mSupportedDevices.clear(); + } + for (size_t i = 0; i < mInputProfiles.size(); i++) { + mInputProfiles[i]->mSupportedDevices.clear(); + } + free((void *)mName); +} + +status_t HwModule::loadInput(cnode *root) +{ + cnode *node = root->first_child; + + sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + profile->loadSamplingRates((char *)node->value); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + profile->loadFormats((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + profile->loadInChannels((char *)node->value); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + mDeclaredDevices); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = ConfigParsingUtils::parseInputFlagNames((char *)node->value); + } else if (strcmp(node->name, GAINS_TAG) == 0) { + profile->loadGains(node); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices.isEmpty(), + "loadInput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadInput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadInput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadInput() invalid supported formats"); + if (!profile->mSupportedDevices.isEmpty() && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadInput() adding input Supported Devices %04x", + profile->mSupportedDevices.types()); + + mInputProfiles.add(profile); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t HwModule::loadOutput(cnode *root) +{ + cnode *node = root->first_child; + + sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this); + + while (node) { + if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { + profile->loadSamplingRates((char *)node->value); + } else if (strcmp(node->name, FORMATS_TAG) == 0) { + profile->loadFormats((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + profile->loadOutChannels((char *)node->value); + } else if (strcmp(node->name, DEVICES_TAG) == 0) { + profile->mSupportedDevices.loadDevicesFromName((char *)node->value, + mDeclaredDevices); + } else if (strcmp(node->name, FLAGS_TAG) == 0) { + profile->mFlags = ConfigParsingUtils::parseOutputFlagNames((char *)node->value); + } else if (strcmp(node->name, GAINS_TAG) == 0) { + profile->loadGains(node); + } + node = node->next; + } + ALOGW_IF(profile->mSupportedDevices.isEmpty(), + "loadOutput() invalid supported devices"); + ALOGW_IF(profile->mChannelMasks.size() == 0, + "loadOutput() invalid supported channel masks"); + ALOGW_IF(profile->mSamplingRates.size() == 0, + "loadOutput() invalid supported sampling rates"); + ALOGW_IF(profile->mFormats.size() == 0, + "loadOutput() invalid supported formats"); + if (!profile->mSupportedDevices.isEmpty() && + (profile->mChannelMasks.size() != 0) && + (profile->mSamplingRates.size() != 0) && + (profile->mFormats.size() != 0)) { + + ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x", + profile->mSupportedDevices.types(), profile->mFlags); + + mOutputProfiles.add(profile); + return NO_ERROR; + } else { + return BAD_VALUE; + } +} + +status_t HwModule::loadDevice(cnode *root) +{ + cnode *node = root->first_child; + + audio_devices_t type = AUDIO_DEVICE_NONE; + while (node) { + if (strcmp(node->name, APM_DEVICE_TYPE) == 0) { + type = ConfigParsingUtils::parseDeviceNames((char *)node->value); + break; + } + node = node->next; + } + if (type == AUDIO_DEVICE_NONE || + (!audio_is_input_device(type) && !audio_is_output_device(type))) { + ALOGW("loadDevice() bad type %08x", type); + return BAD_VALUE; + } + sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type); + deviceDesc->mModule = this; + + node = root->first_child; + while (node) { + if (strcmp(node->name, APM_DEVICE_ADDRESS) == 0) { + deviceDesc->mAddress = String8((char *)node->value); + } else if (strcmp(node->name, CHANNELS_TAG) == 0) { + if (audio_is_input_device(type)) { + deviceDesc->loadInChannels((char *)node->value); + } else { + deviceDesc->loadOutChannels((char *)node->value); + } + } else if (strcmp(node->name, GAINS_TAG) == 0) { + deviceDesc->loadGains(node); + } + node = node->next; + } + + ALOGV("loadDevice() adding device name %s type %08x address %s", + deviceDesc->mName.string(), type, deviceDesc->mAddress.string()); + + mDeclaredDevices.add(deviceDesc); + + return NO_ERROR; +} + +status_t HwModule::addOutputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address) +{ + sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this); + + profile->mSamplingRates.add(config->sample_rate); + profile->mChannelMasks.add(config->channel_mask); + profile->mFormats.add(config->format); + + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + devDesc->mAddress = address; + profile->mSupportedDevices.add(devDesc); + + mOutputProfiles.add(profile); + + return NO_ERROR; +} + +status_t HwModule::removeOutputProfile(String8 name) +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + if (mOutputProfiles[i]->mName == name) { + mOutputProfiles.removeAt(i); + break; + } + } + + return NO_ERROR; +} + +status_t HwModule::addInputProfile(String8 name, const audio_config_t *config, + audio_devices_t device, String8 address) +{ + sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this); + + profile->mSamplingRates.add(config->sample_rate); + profile->mChannelMasks.add(config->channel_mask); + profile->mFormats.add(config->format); + + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(name, device); + devDesc->mAddress = address; + profile->mSupportedDevices.add(devDesc); + + ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask); + + mInputProfiles.add(profile); + + return NO_ERROR; +} + +status_t HwModule::removeInputProfile(String8 name) +{ + for (size_t i = 0; i < mInputProfiles.size(); i++) { + if (mInputProfiles[i]->mName == name) { + mInputProfiles.removeAt(i); + break; + } + } + + return NO_ERROR; +} + + +void HwModule::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - name: %s\n", mName); + result.append(buffer); + snprintf(buffer, SIZE, " - handle: %d\n", mHandle); + result.append(buffer); + snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mOutputProfiles.size()) { + write(fd, " - outputs:\n", strlen(" - outputs:\n")); + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + snprintf(buffer, SIZE, " output %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mOutputProfiles[i]->dump(fd); + } + } + if (mInputProfiles.size()) { + write(fd, " - inputs:\n", strlen(" - inputs:\n")); + for (size_t i = 0; i < mInputProfiles.size(); i++) { + snprintf(buffer, SIZE, " input %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mInputProfiles[i]->dump(fd); + } + } + if (mDeclaredDevices.size()) { + write(fd, " - devices:\n", strlen(" - devices:\n")); + for (size_t i = 0; i < mDeclaredDevices.size(); i++) { + mDeclaredDevices[i]->dump(fd, 4, i); + } + } +} + +sp <HwModule> HwModuleCollection::getModuleFromName(const char *name) const +{ + sp <HwModule> module; + + for (size_t i = 0; i < size(); i++) + { + if (strcmp(itemAt(i)->mName, name) == 0) { + return itemAt(i); + } + } + return module; +} + + +sp <HwModule> HwModuleCollection::getModuleForDevice(audio_devices_t device) const +{ + sp <HwModule> module; + + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mHandle == 0) { + continue; + } + if (audio_is_output_device(device)) { + for (size_t j = 0; j < itemAt(i)->mOutputProfiles.size(); j++) + { + if (itemAt(i)->mOutputProfiles[j]->mSupportedDevices.types() & device) { + return itemAt(i); + } + } + } else { + for (size_t j = 0; j < itemAt(i)->mInputProfiles.size(); j++) { + if (itemAt(i)->mInputProfiles[j]->mSupportedDevices.types() & + device & ~AUDIO_DEVICE_BIT_IN) { + return itemAt(i); + } + } + } + } + return module; +} + +sp<DeviceDescriptor> HwModuleCollection::getDeviceDescriptor(const audio_devices_t device, + const char *device_address, + const char *device_name) const +{ + String8 address = (device_address == NULL) ? String8("") : String8(device_address); + // handle legacy remote submix case where the address was not always specified + if (device_distinguishes_on_address(device) && (address.length() == 0)) { + address = String8("0"); + } + + for (size_t i = 0; i < size(); i++) { + const sp<HwModule> hwModule = itemAt(i); + if (hwModule->mHandle == 0) { + continue; + } + DeviceVector deviceList = + hwModule->mDeclaredDevices.getDevicesFromTypeAddr(device, address); + if (!deviceList.isEmpty()) { + return deviceList.itemAt(0); + } + deviceList = hwModule->mDeclaredDevices.getDevicesFromType(device); + if (!deviceList.isEmpty()) { + return deviceList.itemAt(0); + } + } + + sp<DeviceDescriptor> devDesc = + new DeviceDescriptor(String8(device_name != NULL ? device_name : ""), device); + devDesc->mAddress = address; + return devDesc; +} + +status_t HwModuleCollection::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\nHW Modules dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, "- HW Module %zu:\n", i + 1); + write(fd, buffer, strlen(buffer)); + itemAt(i)->dump(fd); + } + return NO_ERROR; +} + +} //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp new file mode 100644 index 0000000..376dd22 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/IOProfile.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2015 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 "APM::IOProfile" +//#define LOG_NDEBUG 0 + +#include "IOProfile.h" +#include "HwModule.h" +#include "AudioGain.h" + +namespace android { + +IOProfile::IOProfile(const String8& name, audio_port_role_t role, + const sp<HwModule>& module) + : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module) +{ +} + +IOProfile::~IOProfile() +{ +} + +// checks if the IO profile is compatible with specified parameters. +// Sampling rate, format and channel mask must be specified in order to +// get a valid a match +bool IOProfile::isCompatibleProfile(audio_devices_t device, + String8 address, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + uint32_t flags) const +{ + const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE; + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + ALOG_ASSERT(isPlaybackThread != isRecordThread); + + + if (device != AUDIO_DEVICE_NONE) { + // just check types if multiple devices are selected + if (popcount(device & ~AUDIO_DEVICE_BIT_IN) > 1) { + if ((mSupportedDevices.types() & device) != device) { + return false; + } + } else if (mSupportedDevices.getDevice(device, address) == 0) { + return false; + } + } + + if (samplingRate == 0) { + return false; + } + uint32_t myUpdatedSamplingRate = samplingRate; + if (isPlaybackThread && checkExactSamplingRate(samplingRate) != NO_ERROR) { + return false; + } + if (isRecordThread && checkCompatibleSamplingRate(samplingRate, &myUpdatedSamplingRate) != + NO_ERROR) { + return false; + } + + if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) { + return false; + } + + if (isPlaybackThread && (!audio_is_output_channel(channelMask) || + checkExactChannelMask(channelMask) != NO_ERROR)) { + return false; + } + if (isRecordThread && (!audio_is_input_channel(channelMask) || + checkCompatibleChannelMask(channelMask) != NO_ERROR)) { + return false; + } + + if (isPlaybackThread && (mFlags & flags) != flags) { + return false; + } + // The only input flag that is allowed to be different is the fast flag. + // An existing fast stream is compatible with a normal track request. + // An existing normal stream is compatible with a fast track request, + // but the fast request will be denied by AudioFlinger and converted to normal track. + if (isRecordThread && ((mFlags ^ flags) & + ~AUDIO_INPUT_FLAG_FAST)) { + return false; + } + + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = myUpdatedSamplingRate; + } + return true; +} + +void IOProfile::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + AudioPort::dump(fd, 4); + + snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " - devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + for (size_t i = 0; i < mSupportedDevices.size(); i++) { + mSupportedDevices[i]->dump(fd, 6, i); + } +} + +void IOProfile::log() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + ALOGV(" - sampling rates: "); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + ALOGV(" %d", mSamplingRates[i]); + } + + ALOGV(" - channel masks: "); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV(" 0x%04x", mChannelMasks[i]); + } + + ALOGV(" - formats: "); + for (size_t i = 0; i < mFormats.size(); i++) { + ALOGV(" 0x%08x", mFormats[i]); + } + + ALOGV(" - devices: 0x%04x\n", mSupportedDevices.types()); + ALOGV(" - flags: 0x%04x\n", mFlags); +} + +}; // namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/SoundTriggerSession.cpp b/services/audiopolicy/common/managerdefinitions/src/SoundTriggerSession.cpp new file mode 100644 index 0000000..8ca3ae0 --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/SoundTriggerSession.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 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 "APM::SoundTriggerSession" +//#define LOG_NDEBUG 0 + +#include "SoundTriggerSession.h" + + +namespace android { + +status_t SoundTriggerSessionCollection::acquireSession(audio_session_t session, + audio_io_handle_t ioHandle) +{ + add(session, ioHandle); + + return NO_ERROR; +} + +status_t SoundTriggerSessionCollection::releaseSession(audio_session_t session) +{ + ssize_t index = indexOfKey(session); + if (index < 0) { + ALOGW("acquireSoundTriggerSession() session %d not registered", session); + return BAD_VALUE; + } + + removeItem(session); + return NO_ERROR; +} + +}; //namespace android diff --git a/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp new file mode 100644 index 0000000..b682e2c --- /dev/null +++ b/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 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 "APM::Volumes" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include "StreamDescriptor.h" +#include <utils/Log.h> +#include <utils/String8.h> + +namespace android { + +// --- StreamDescriptor class implementation + +StreamDescriptor::StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) +{ + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); +} + +int StreamDescriptor::getVolumeIndex(audio_devices_t device) const +{ + device = Volume::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT + if (mIndexCur.indexOfKey(device) < 0) { + device = AUDIO_DEVICE_OUT_DEFAULT; + } + return mIndexCur.valueFor(device); +} + +void StreamDescriptor::clearCurrentVolumeIndex() +{ + mIndexCur.clear(); +} + +void StreamDescriptor::addCurrentVolumeIndex(audio_devices_t device, int index) +{ + mIndexCur.add(device, index); +} + +void StreamDescriptor::setVolumeIndexMin(int volIndexMin) +{ + mIndexMin = volIndexMin; +} + +void StreamDescriptor::setVolumeIndexMax(int volIndexMax) +{ + mIndexMax = volIndexMax; +} + +void StreamDescriptor::setVolumeCurvePoint(Volume::device_category deviceCategory, + const VolumeCurvePoint *point) +{ + mVolumeCurve[deviceCategory] = point; +} + +void StreamDescriptor::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%s %02d %02d ", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + result.append(buffer); + for (size_t i = 0; i < mIndexCur.size(); i++) { + snprintf(buffer, SIZE, "%04x : %02d, ", + mIndexCur.keyAt(i), + mIndexCur.valueAt(i)); + result.append(buffer); + } + result.append("\n"); + + write(fd, result.string(), result.size()); +} + +StreamDescriptorCollection::StreamDescriptorCollection() +{ + for (size_t stream = 0 ; stream < AUDIO_STREAM_CNT; stream++) { + add(static_cast<audio_stream_type_t>(stream), StreamDescriptor()); + } +} + +bool StreamDescriptorCollection::canBeMuted(audio_stream_type_t stream) +{ + return valueAt(stream).canBeMuted(); +} + +void StreamDescriptorCollection::clearCurrentVolumeIndex(audio_stream_type_t stream) +{ + editValueAt(stream).clearCurrentVolumeIndex(); +} + +void StreamDescriptorCollection::addCurrentVolumeIndex(audio_stream_type_t stream, + audio_devices_t device, int index) +{ + editValueAt(stream).addCurrentVolumeIndex(device, index); +} + +void StreamDescriptorCollection::setVolumeCurvePoint(audio_stream_type_t stream, + Volume::device_category deviceCategory, + const VolumeCurvePoint *point) +{ + editValueAt(stream).setVolumeCurvePoint(deviceCategory, point); +} + +const VolumeCurvePoint *StreamDescriptorCollection::getVolumeCurvePoint(audio_stream_type_t stream, + Volume::device_category deviceCategory) const +{ + return valueAt(stream).getVolumeCurvePoint(deviceCategory); +} + +void StreamDescriptorCollection::setVolumeIndexMin(audio_stream_type_t stream,int volIndexMin) +{ + return editValueAt(stream).setVolumeIndexMin(volIndexMin); +} + +void StreamDescriptorCollection::setVolumeIndexMax(audio_stream_type_t stream,int volIndexMax) +{ + return editValueAt(stream).setVolumeIndexMax(volIndexMax); +} + +status_t StreamDescriptorCollection::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "\nStreams dump:\n"); + write(fd, buffer, strlen(buffer)); + snprintf(buffer, SIZE, + " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < size(); i++) { + snprintf(buffer, SIZE, " %02zu ", i); + write(fd, buffer, strlen(buffer)); + valueAt(i).dump(fd); + } + + return NO_ERROR; +} + +}; // namespace android diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h new file mode 100755 index 0000000..eadaa77 --- /dev/null +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <AudioPolicyManagerObserver.h> +#include <RoutingStrategy.h> +#include <Volume.h> +#include <HwModule.h> +#include <DeviceDescriptor.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <utils/Errors.h> +#include <utils/Vector.h> + +namespace android { + +/** + * This interface is dedicated to the policy manager that a Policy Engine shall implement. + */ +class AudioPolicyManagerInterface +{ +public: + /** + * Checks if the engine was correctly initialized. + * + * @return NO_ERROR if initialization has been done correctly, error code otherwise.. + */ + virtual status_t initCheck() = 0; + + /** + * Sets the Manager observer that allows the engine to retrieve information on collection + * of devices, streams, HwModules, ... + * + * @param[in] observer handle on the manager. + */ + virtual void setObserver(AudioPolicyManagerObserver *observer) = 0; + + /** + * Get the input device selected for a given input source. + * + * @param[in] inputSource to get the selected input device associated to + * + * @return selected input device for the given input source, may be none if error. + */ + virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const = 0; + + /** + * Get the output device associated to a given strategy. + * + * @param[in] stream type for which the selected ouput device is requested. + * + * @return selected ouput device for the given strategy, may be none if error. + */ + virtual audio_devices_t getDeviceForStrategy(routing_strategy stategy) const = 0; + + /** + * Get the strategy selected for a given stream type. + * + * @param[in] stream: for which the selected strategy followed by is requested. + * + * @return strategy to be followed. + */ + virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) = 0; + + /** + * Get the strategy selected for a given usage. + * + * @param[in] usage to get the selected strategy followed by. + * + * @return strategy to be followed. + */ + virtual routing_strategy getStrategyForUsage(audio_usage_t usage) = 0; + + /** + * Set the Telephony Mode. + * + * @param[in] mode: Android Phone state (normal, ringtone, csv, in communication) + * + * @return NO_ERROR if Telephony Mode set correctly, error code otherwise. + */ + virtual status_t setPhoneState(audio_mode_t mode) = 0; + + /** + * Get the telephony Mode + * + * @return the current telephony mode + */ + virtual audio_mode_t getPhoneState() const = 0; + + /** + * Set Force Use config for a given usage. + * + * @param[in] usage for which a configuration shall be forced. + * @param[in] config wished to be forced for the given usage. + * + * @return NO_ERROR if the Force Use config was set correctly, error code otherwise (e.g. config not + * allowed a given usage...) + */ + virtual status_t setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) = 0; + + /** + * Get Force Use config for a given usage. + * + * @param[in] usage for which a configuration shall be forced. + * + * @return config wished to be forced for the given usage. + */ + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const = 0; + + /** + * Set the connection state of device(s). + * + * @param[in] devDesc for which the state has changed. + * @param[in] state of availability of this(these) device(s). + * + * @return NO_ERROR if devices criterion updated correctly, error code otherwise. + */ + virtual status_t setDeviceConnectionState(const android::sp<android::DeviceDescriptor> devDesc, + audio_policy_dev_state_t state) = 0; + + /** + * Translate a volume index given by the UI to an amplification value for a stream type + * and a device category. + * + * @param[in] deviceCategory for which the conversion is requested. + * @param[in] stream type for which the conversion is requested. + * @param[in] indexInUi index received from the UI to be translated. + * + * @return amplification value matching the UI index for this given device and stream. + */ + virtual float volIndexToAmpl(Volume::device_category deviceCategory, audio_stream_type_t stream, + int indexInUi) = 0; + + /** + * Initialize the min / max index of volume applicable for a given stream type. These indexes + * will be used upon conversion of UI index to volume amplification. + * + * @param[in] stream type for which the indexes need to be set + * @param[in] indexMin Minimum index allowed for this stream. + * @param[in] indexMax Maximum index allowed for this stream. + */ + virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) = 0; + + /** + * Initialize volume curves for each strategy and device category + * + * @param[in] isSpeakerDrcEnabled true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER + path to boost soft sounds, used to adjust volume curves accordingly + */ + virtual void initializeVolumeCurves(bool isSpeakerDrcEnabled) = 0; + +protected: + virtual ~AudioPolicyManagerInterface() {} +}; + +}; // namespace android diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h new file mode 100755 index 0000000..4f5427e --- /dev/null +++ b/services/audiopolicy/engine/interface/AudioPolicyManagerObserver.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <AudioGain.h> +#include <AudioPort.h> +#include <AudioPatch.h> +#include <IOProfile.h> +#include <DeviceDescriptor.h> +#include <AudioInputDescriptor.h> +#include <AudioOutputDescriptor.h> +#include <AudioPolicyMix.h> +#include <SoundTriggerSession.h> +#include <StreamDescriptor.h> + +namespace android { + +/** + * This interface is an observer that the manager shall implement to allows e.g. the engine + * to access to policy pillars elements (like output / input descritors collections, + * HwModule collections, AudioMix, ... + */ +class AudioPolicyManagerObserver +{ +public: + virtual const AudioPatchCollection &getAudioPatches() const = 0; + + virtual const SoundTriggerSessionCollection &getSoundTriggerSessionCollection() const = 0; + + virtual const AudioPolicyMixCollection &getAudioPolicyMixCollection() const = 0; + + virtual const AudioOutputCollection &getOutputs() const = 0; + + virtual const AudioInputCollection &getInputs() const = 0; + + virtual const DeviceVector &getAvailableOutputDevices() const = 0; + + virtual const DeviceVector &getAvailableInputDevices() const = 0; + + virtual StreamDescriptorCollection &getStreamDescriptors() = 0; + + virtual const sp<DeviceDescriptor> &getDefaultOutputDevice() const = 0; + +protected: + virtual ~AudioPolicyManagerObserver() {} +}; + +}; // namespace android diff --git a/services/audiopolicy/enginedefault/Android.mk b/services/audiopolicy/enginedefault/Android.mk new file mode 100755 index 0000000..b0ae835 --- /dev/null +++ b/services/audiopolicy/enginedefault/Android.mk @@ -0,0 +1,48 @@ +LOCAL_PATH := $(call my-dir) + +# Component build +####################################################################### + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + src/Engine.cpp \ + src/EngineInstance.cpp \ + src/Gains.cpp \ + + +audio_policy_engine_includes_common := \ + $(LOCAL_PATH)/include \ + $(TOPDIR)frameworks/av/services/audiopolicy/engine/interface + +LOCAL_CFLAGS += \ + -Wall \ + -Werror \ + -Wextra \ + +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(audio_policy_engine_includes_common) + +LOCAL_C_INCLUDES := \ + $(audio_policy_engine_includes_common) \ + $(TARGET_OUT_HEADERS)/hw \ + $(call include-path-for, frameworks-av) \ + $(call include-path-for, audio-utils) \ + $(call include-path-for, bionic) \ + $(TOPDIR)frameworks/av/services/audiopolicy/common/include + + +LOCAL_MODULE := libaudiopolicyenginedefault +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper \ + libaudiopolicycomponents + +LOCAL_SHARED_LIBRARIES += \ + libcutils \ + libutils \ + libaudioutils \ + +include external/stlport/libstlport.mk + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audiopolicy/enginedefault/include/AudioPolicyEngineInstance.h b/services/audiopolicy/enginedefault/include/AudioPolicyEngineInstance.h new file mode 100755 index 0000000..1e329f0 --- /dev/null +++ b/services/audiopolicy/enginedefault/include/AudioPolicyEngineInstance.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +class AudioPolicyManagerInterface; + +namespace android +{ +namespace audio_policy +{ + +class Engine; + +class EngineInstance +{ +protected: + EngineInstance(); + +public: + virtual ~EngineInstance(); + + /** + * Get Audio Policy Engine instance. + * + * @return pointer to Route Manager Instance object. + */ + static EngineInstance *getInstance(); + + /** + * Interface query. + * The first client of an interface of the policy engine will start the singleton. + * + * @tparam RequestedInterface: interface that the client is wishing to retrieve. + * + * @return interface handle. + */ + template <class RequestedInterface> + RequestedInterface *queryInterface() const; + +protected: + /** + * Get Audio Policy Engine instance. + * + * @return Audio Policy Engine singleton. + */ + Engine *getEngine() const; + +private: + /* Copy facilities are put private to disable copy. */ + EngineInstance(const EngineInstance &object); + EngineInstance &operator=(const EngineInstance &object); +}; + +/** + * Limit template instantation to supported type interfaces. + * Compile time error will claim if invalid interface is requested. + */ +template <> +AudioPolicyManagerInterface *EngineInstance::queryInterface() const; + +} // namespace audio_policy +} // namespace android diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp new file mode 100755 index 0000000..1fd3341 --- /dev/null +++ b/services/audiopolicy/enginedefault/src/Engine.cpp @@ -0,0 +1,707 @@ +/* + * Copyright (C) 2015 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 "APM::AudioPolicyEngine" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include "Engine.h" +#include "Gains.h" +#include <AudioPolicyManagerObserver.h> +#include <AudioPort.h> +#include <IOProfile.h> +#include <policy.h> +#include <utils/String8.h> +#include <utils/Log.h> + +namespace android +{ +namespace audio_policy +{ + +Engine::Engine() + : mManagerInterface(this), + mPhoneState(AUDIO_MODE_NORMAL), + mApmObserver(NULL) +{ + for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) { + mForceUse[i] = AUDIO_POLICY_FORCE_NONE; + } +} + +Engine::~Engine() +{ +} + +void Engine::setObserver(AudioPolicyManagerObserver *observer) +{ + ALOG_ASSERT(observer != NULL, "Invalid Audio Policy Manager observer"); + mApmObserver = observer; +} + +status_t Engine::initCheck() +{ + return (mApmObserver != NULL) ? NO_ERROR : NO_INIT; +} + +float Engine::volIndexToAmpl(Volume::device_category category, audio_stream_type_t streamType, + int indexInUi) +{ + const StreamDescriptor &streamDesc = mApmObserver->getStreamDescriptors().valueAt(streamType); + return Gains::volIndexToAmpl(category, streamDesc, indexInUi); +} + +status_t Engine::initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) +{ + ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMin >= indexMax) { + ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", + stream , indexMin, indexMax); + return BAD_VALUE; + } + mApmObserver->getStreamDescriptors().setVolumeIndexMin(stream, indexMin); + mApmObserver->getStreamDescriptors().setVolumeIndexMax(stream, indexMax); + return NO_ERROR; +} + +void Engine::initializeVolumeCurves(bool isSpeakerDrcEnabled) +{ + StreamDescriptorCollection &streams = mApmObserver->getStreamDescriptors(); + + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + for (int j = 0; j < Volume::DEVICE_CATEGORY_CNT; j++) { + streams.setVolumeCurvePoint(static_cast<audio_stream_type_t>(i), + static_cast<Volume::device_category>(j), + Gains::sVolumeProfiles[i][j]); + } + } + + // Check availability of DRC on speaker path: if available, override some of the speaker curves + if (isSpeakerDrcEnabled) { + streams.setVolumeCurvePoint(AUDIO_STREAM_SYSTEM, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sDefaultSystemVolumeCurveDrc); + streams.setVolumeCurvePoint(AUDIO_STREAM_RING, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sSpeakerSonificationVolumeCurveDrc); + streams.setVolumeCurvePoint(AUDIO_STREAM_ALARM, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sSpeakerSonificationVolumeCurveDrc); + streams.setVolumeCurvePoint(AUDIO_STREAM_NOTIFICATION, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sSpeakerSonificationVolumeCurveDrc); + streams.setVolumeCurvePoint(AUDIO_STREAM_MUSIC, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sSpeakerMediaVolumeCurveDrc); + streams.setVolumeCurvePoint(AUDIO_STREAM_ACCESSIBILITY, Volume::DEVICE_CATEGORY_SPEAKER, + Gains::sSpeakerMediaVolumeCurveDrc); + } +} + + +status_t Engine::setPhoneState(audio_mode_t state) +{ + ALOGV("setPhoneState() state %d", state); + + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return BAD_VALUE; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return BAD_VALUE; + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + StreamDescriptorCollection &streams = mApmObserver->getStreamDescriptors(); + // are we entering or starting a call + if (!is_state_in_call(oldState) && is_state_in_call(state)) { + ALOGV(" Entering call in setPhoneState()"); + for (int j = 0; j < Volume::DEVICE_CATEGORY_CNT; j++) { + streams.setVolumeCurvePoint(AUDIO_STREAM_DTMF, static_cast<Volume::device_category>(j), + Gains::sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]); + } + } else if (is_state_in_call(oldState) && !is_state_in_call(state)) { + ALOGV(" Exiting call in setPhoneState()"); + for (int j = 0; j < Volume::DEVICE_CATEGORY_CNT; j++) { + streams.setVolumeCurvePoint(AUDIO_STREAM_DTMF, static_cast<Volume::device_category>(j), + Gains::sVolumeProfiles[AUDIO_STREAM_DTMF][j]); + } + } + return NO_ERROR; +} + +status_t Engine::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) +{ + switch(usage) { + case AUDIO_POLICY_FORCE_FOR_COMMUNICATION: + if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return BAD_VALUE; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_MEDIA: + if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_NO_BT_A2DP && config != AUDIO_POLICY_FORCE_SPEAKER ) { + ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return BAD_VALUE; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_RECORD: + if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return BAD_VALUE; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_DOCK: + if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && + config != AUDIO_POLICY_FORCE_BT_DESK_DOCK && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { + ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_SYSTEM: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) { + ALOGW("setForceUse() invalid config %d forHDMI_SYSTEM_AUDIO", config); + } + mForceUse[usage] = config; + break; + default: + ALOGW("setForceUse() invalid usage %d", usage); + break; + } + return NO_ERROR; +} + +routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) +{ + // stream to strategy mapping + switch (stream) { + case AUDIO_STREAM_VOICE_CALL: + case AUDIO_STREAM_BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AUDIO_STREAM_RING: + case AUDIO_STREAM_ALARM: + return STRATEGY_SONIFICATION; + case AUDIO_STREAM_NOTIFICATION: + return STRATEGY_SONIFICATION_RESPECTFUL; + case AUDIO_STREAM_DTMF: + return STRATEGY_DTMF; + default: + ALOGE("unknown stream type %d", stream); + case AUDIO_STREAM_SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AUDIO_STREAM_MUSIC: + return STRATEGY_MEDIA; + case AUDIO_STREAM_ENFORCED_AUDIBLE: + return STRATEGY_ENFORCED_AUDIBLE; + case AUDIO_STREAM_TTS: + return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; + case AUDIO_STREAM_ACCESSIBILITY: + return STRATEGY_ACCESSIBILITY; + case AUDIO_STREAM_REROUTING: + return STRATEGY_REROUTING; + } +} + +routing_strategy Engine::getStrategyForUsage(audio_usage_t usage) +{ + const AudioOutputCollection &outputs = mApmObserver->getOutputs(); + + // usage to strategy mapping + switch (usage) { + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + if (outputs.isStreamActive(AUDIO_STREAM_RING) || + outputs.isStreamActive(AUDIO_STREAM_ALARM)) { + return STRATEGY_SONIFICATION; + } + if (isInCall()) { + return STRATEGY_PHONE; + } + return STRATEGY_ACCESSIBILITY; + + case AUDIO_USAGE_MEDIA: + case AUDIO_USAGE_GAME: + case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case AUDIO_USAGE_ASSISTANCE_SONIFICATION: + return STRATEGY_MEDIA; + + case AUDIO_USAGE_VOICE_COMMUNICATION: + return STRATEGY_PHONE; + + case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: + return STRATEGY_DTMF; + + case AUDIO_USAGE_ALARM: + case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + return STRATEGY_SONIFICATION; + + case AUDIO_USAGE_NOTIFICATION: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case AUDIO_USAGE_NOTIFICATION_EVENT: + return STRATEGY_SONIFICATION_RESPECTFUL; + + case AUDIO_USAGE_UNKNOWN: + default: + return STRATEGY_MEDIA; + } +} + +audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const +{ + const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices(); + const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices(); + + const AudioOutputCollection &outputs = mApmObserver->getOutputs(); + + uint32_t device = AUDIO_DEVICE_NONE; + uint32_t availableOutputDevicesType = availableOutputDevices.types(); + + switch (strategy) { + + case STRATEGY_TRANSMITTED_THROUGH_SPEAKER: + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; + if (!device) { + ALOGE("getDeviceForStrategy() no device found for "\ + "STRATEGY_TRANSMITTED_THROUGH_SPEAKER"); + } + break; + + case STRATEGY_SONIFICATION_RESPECTFUL: + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION); + } else if (outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing on a remote device, use the the sonification behavior. + // Note that we test this usecase before testing if media is playing because + // the isStreamActive() method only informs about the activity of a stream, not + // if it's for local playback. Note also that we use the same delay between both tests + device = getDeviceForStrategy(STRATEGY_SONIFICATION); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } else if (outputs.isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing (or has recently played), use the same device + device = getDeviceForStrategy(STRATEGY_MEDIA); + } else { + // when media is not playing anymore, fall back on the sonification behavior + device = getDeviceForStrategy(STRATEGY_SONIFICATION); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } + break; + + case STRATEGY_DTMF: + if (!isInCall()) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // Force use of only devices on primary output if: + // - in call AND + // - cannot route from voice call RX OR + // - audio HAL version is < 3.0 and TX device is on the primary HW module + if (getPhoneState() == AUDIO_MODE_IN_CALL) { + audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput(); + audio_devices_t availPrimaryInputDevices = + availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle()); + audio_devices_t availPrimaryOutputDevices = + primaryOutput->supportedDevices() & availableOutputDevices.types(); + + if (((availableInputDevices.types() & + AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || + (((txDevice & availPrimaryInputDevices & ~AUDIO_DEVICE_BIT_IN) != 0) && + (primaryOutput->getAudioPort()->mModule->mHalVersion < + AUDIO_DEVICE_API_VERSION_3_0))) { + availableOutputDevicesType = availPrimaryOutputDevices; + } + } + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + if (!isInCall() || strategy != STRATEGY_DTMF) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + if (device) break; + // if SCO device is requested but no SCO device is available, fall back to default case + // FALL THROUGH + + default: // FORCE_NONE + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (outputs.getA2dpOutput() != 0)) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + if (!isInCall()) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_EARPIECE; + if (device) break; + device = mApmObserver->getDefaultOutputDevice()->type(); + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (outputs.getA2dpOutput() != 0)) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } + if (!isInCall()) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE; + if (device) break; + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; + if (device) break; + device = mApmObserver->getDefaultOutputDevice()->type(); + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); + } + break; + } + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_PHONE); + break; + } + // FALL THROUGH + + case STRATEGY_ENFORCED_AUDIBLE: + // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + // except: + // - when in call where it doesn't default to STRATEGY_PHONE behavior + // - in countries where not enforced in which case it follows STRATEGY_MEDIA + + if ((strategy == STRATEGY_SONIFICATION) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { + device = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); + } + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + // FIXME: STRATEGY_ACCESSIBILITY and STRATEGY_REROUTING follow STRATEGY_MEDIA for now + case STRATEGY_ACCESSIBILITY: + if (strategy == STRATEGY_ACCESSIBILITY) { + // do not route accessibility prompts to a digital output currently configured with a + // compressed format as they would likely not be mixed and dropped. + for (size_t i = 0; i < outputs.size(); i++) { + sp<AudioOutputDescriptor> desc = outputs.valueAt(i); + audio_devices_t devices = desc->device() & + (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC); + if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) && + devices != AUDIO_DEVICE_NONE) { + availableOutputDevicesType = availableOutputDevices.types() & ~devices; + } + } + } + // FALL THROUGH + + case STRATEGY_REROUTING: + case STRATEGY_MEDIA: { + uint32_t device2 = AUDIO_DEVICE_NONE; + if (strategy != STRATEGY_SONIFICATION) { + // no sonification on remote submix (e.g. WFD) + if (availableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) { + device2 = availableOutputDevices.types() & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (outputs.getA2dpOutput() != 0)) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_LINE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_ACCESSORY; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_USB_DEVICE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { + // no sonification on aux digital (e.g. HDMI) + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_DIGITAL; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER; + } + int device3 = AUDIO_DEVICE_NONE; + if (strategy == STRATEGY_MEDIA) { + // ARC, SPDIF and AUX_LINE can co-exist with others. + device3 = availableOutputDevicesType & AUDIO_DEVICE_OUT_HDMI_ARC; + device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPDIF); + device3 |= (availableOutputDevicesType & AUDIO_DEVICE_OUT_AUX_LINE); + } + + device2 |= device3; + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or + // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise + device |= device2; + + // If hdmi system audio mode is on, remove speaker out of output list. + if ((strategy == STRATEGY_MEDIA) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == + AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { + device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } + + if (device) break; + device = mApmObserver->getDefaultOutputDevice()->type(); + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); + } + } break; + + default: + ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + + +audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const +{ + const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices(); + const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices(); + const AudioOutputCollection &outputs = mApmObserver->getOutputs(); + audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; + + uint32_t device = AUDIO_DEVICE_NONE; + + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + break; + } + break; + + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + } else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) && + (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_SOURCE_VOICE_COMMUNICATION: + // Allow only use of devices on primary input if in call and HAL does not support routing + // to voice call path. + if ((getPhoneState() == AUDIO_MODE_IN_CALL) && + (availableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { + sp<AudioOutputDescriptor> primaryOutput = outputs.getPrimaryOutput(); + availableDeviceTypes = + availableInputDevices.getDevicesFromHwModule(primaryOutput->getModuleHandle()) + & ~AUDIO_DEVICE_BIT_IN; + } + + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + // if SCO device is requested but no SCO device is available, fall back to default case + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + break; + } + // FALL THROUGH + + default: // FORCE_NONE + if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + } + break; + + case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_HOTWORD: + if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && + availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + } + break; + case AUDIO_SOURCE_REMOTE_SUBMIX: + if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + case AUDIO_SOURCE_FM_TUNER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) { + device = AUDIO_DEVICE_IN_FM_TUNER; + } + break; + default: + ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); + break; + } + ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +template <> +AudioPolicyManagerInterface *Engine::queryInterface() +{ + return &mManagerInterface; +} + +} // namespace audio_policy +} // namespace android + + diff --git a/services/audiopolicy/enginedefault/src/Engine.h b/services/audiopolicy/enginedefault/src/Engine.h new file mode 100755 index 0000000..f44556c --- /dev/null +++ b/services/audiopolicy/enginedefault/src/Engine.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + + +#include "AudioPolicyManagerInterface.h" +#include "Gains.h" +#include <AudioGain.h> +#include <policy.h> + +namespace android +{ + +class AudioPolicyManagerObserver; + +namespace audio_policy +{ + +class Engine +{ +public: + Engine(); + virtual ~Engine(); + + template <class RequestedInterface> + RequestedInterface *queryInterface(); + +private: + /// Interface members + class ManagerInterfaceImpl : public AudioPolicyManagerInterface + { + public: + ManagerInterfaceImpl(Engine *policyEngine) + : mPolicyEngine(policyEngine) {} + + virtual void setObserver(AudioPolicyManagerObserver *observer) + { + mPolicyEngine->setObserver(observer); + } + virtual status_t initCheck() + { + return mPolicyEngine->initCheck(); + } + virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const + { + return mPolicyEngine->getDeviceForInputSource(inputSource); + } + virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy) const + { + return mPolicyEngine->getDeviceForStrategy(strategy); + } + virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) + { + return mPolicyEngine->getStrategyForStream(stream); + } + virtual routing_strategy getStrategyForUsage(audio_usage_t usage) + { + return mPolicyEngine->getStrategyForUsage(usage); + } + virtual status_t setPhoneState(audio_mode_t mode) + { + return mPolicyEngine->setPhoneState(mode); + } + virtual audio_mode_t getPhoneState() const + { + return mPolicyEngine->getPhoneState(); + } + virtual status_t setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) + { + return mPolicyEngine->setForceUse(usage, config); + } + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const + { + return mPolicyEngine->getForceUse(usage); + } + virtual status_t setDeviceConnectionState(const sp<DeviceDescriptor> /*devDesc*/, + audio_policy_dev_state_t /*state*/) + { + return NO_ERROR; + } + virtual status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax) + { + return mPolicyEngine->initStreamVolume(stream, indexMin, indexMax); + } + virtual void initializeVolumeCurves(bool isSpeakerDrcEnabled) + { + return mPolicyEngine->initializeVolumeCurves(isSpeakerDrcEnabled); + } + virtual float volIndexToAmpl(Volume::device_category deviceCategory, + audio_stream_type_t stream,int indexInUi) + { + return mPolicyEngine->volIndexToAmpl(deviceCategory, stream, indexInUi); + } + private: + Engine *mPolicyEngine; + } mManagerInterface; + +private: + /* Copy facilities are put private to disable copy. */ + Engine(const Engine &object); + Engine &operator=(const Engine &object); + + void setObserver(AudioPolicyManagerObserver *observer); + + status_t initCheck(); + + inline bool isInCall() const + { + return is_state_in_call(mPhoneState); + } + + status_t setPhoneState(audio_mode_t mode); + audio_mode_t getPhoneState() const + { + return mPhoneState; + } + status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); + audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const + { + return mForceUse[usage]; + } + status_t setDefaultDevice(audio_devices_t device); + + routing_strategy getStrategyForStream(audio_stream_type_t stream); + routing_strategy getStrategyForUsage(audio_usage_t usage); + audio_devices_t getDeviceForStrategy(routing_strategy strategy) const; + audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const; + + float volIndexToAmpl(Volume::device_category category, + audio_stream_type_t stream, int indexInUi); + status_t initStreamVolume(audio_stream_type_t stream, int indexMin, int indexMax); + void initializeVolumeCurves(bool isSpeakerDrcEnabled); + + audio_mode_t mPhoneState; /**< current phone state. */ + + /** current forced use configuration. */ + audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; + + AudioPolicyManagerObserver *mApmObserver; +}; +} // namespace audio_policy +} // namespace android + diff --git a/services/audiopolicy/enginedefault/src/EngineInstance.cpp b/services/audiopolicy/enginedefault/src/EngineInstance.cpp new file mode 100755 index 0000000..17e9832 --- /dev/null +++ b/services/audiopolicy/enginedefault/src/EngineInstance.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 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. + */ + +#include <AudioPolicyManagerInterface.h> +#include "AudioPolicyEngineInstance.h" +#include "Engine.h" + +namespace android +{ +namespace audio_policy +{ + +EngineInstance::EngineInstance() +{ +} + +EngineInstance *EngineInstance::getInstance() +{ + static EngineInstance instance; + return &instance; +} + +EngineInstance::~EngineInstance() +{ +} + +Engine *EngineInstance::getEngine() const +{ + static Engine engine; + return &engine; +} + +template <> +AudioPolicyManagerInterface *EngineInstance::queryInterface() const +{ + return getEngine()->queryInterface<AudioPolicyManagerInterface>(); +} + +} // namespace audio_policy +} // namespace android + diff --git a/services/audiopolicy/enginedefault/src/Gains.cpp b/services/audiopolicy/enginedefault/src/Gains.cpp new file mode 100644 index 0000000..a684fdd --- /dev/null +++ b/services/audiopolicy/enginedefault/src/Gains.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2015 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 "APM::Gains" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +#include "Gains.h" +#include <Volume.h> +#include <math.h> +#include <utils/String8.h> + +namespace android { + +// Enginedefault +const VolumeCurvePoint +Gains::sDefaultVolumeCurve[Volume::VOLCNT] = { + {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} +}; + + +const VolumeCurvePoint +Gains::sDefaultMediaVolumeCurve[Volume::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sExtMediaSystemVolumeCurve[Volume::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} +}; + +const VolumeCurvePoint +Gains::sSpeakerMediaVolumeCurve[Volume::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sSpeakerMediaVolumeCurveDrc[Volume::VOLCNT] = { + {1, -55.0f}, {20, -43.0f}, {86, -12.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sSpeakerSonificationVolumeCurve[Volume::VOLCNT] = { + {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sSpeakerSonificationVolumeCurveDrc[Volume::VOLCNT] = { + {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} +}; + +// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks +// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. +// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). +// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. + +const VolumeCurvePoint +Gains::sDefaultSystemVolumeCurve[Volume::VOLCNT] = { + {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} +}; + +const VolumeCurvePoint +Gains::sDefaultSystemVolumeCurveDrc[Volume::VOLCNT] = { + {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} +}; + +const VolumeCurvePoint +Gains::sHeadsetSystemVolumeCurve[Volume::VOLCNT] = { + {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} +}; + +const VolumeCurvePoint +Gains::sDefaultVoiceVolumeCurve[Volume::VOLCNT] = { + {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sSpeakerVoiceVolumeCurve[Volume::VOLCNT] = { + {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sLinearVolumeCurve[Volume::VOLCNT] = { + {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint +Gains::sSilentVolumeCurve[Volume::VOLCNT] = { + {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.0f} +}; + +const VolumeCurvePoint +Gains::sFullScaleVolumeCurve[Volume::VOLCNT] = { + {0, 0.0f}, {1, 0.0f}, {2, 0.0f}, {100, 0.0f} +}; + +const VolumeCurvePoint *Gains::sVolumeProfiles[AUDIO_STREAM_CNT] + [Volume::DEVICE_CATEGORY_CNT] = { + { // AUDIO_STREAM_VOICE_CALL + Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_SYSTEM + Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_RING + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_MUSIC + Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ALARM + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_NOTIFICATION + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_BLUETOOTH_SCO + Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ENFORCED_AUDIBLE + Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_DTMF + Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_TTS + // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER + Gains::sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sLinearVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sSilentVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ACCESSIBILITY + Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_REROUTING + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_PATCH + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER + Gains::sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE + Gains::sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, +}; + +//static +float Gains::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + Volume::device_category deviceCategory = Volume::getDeviceCategory(device); + const VolumeCurvePoint *curve = streamDesc.getVolumeCurvePoint(deviceCategory); + + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + curve[Volume::VOLMAX].mIndex - + curve[Volume::VOLMIN].mIndex; + int volIdx = (nbSteps * (indexInUi - streamDesc.getVolumeIndexMin())) / + (streamDesc.getVolumeIndexMax() - streamDesc.getVolumeIndexMin()); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < curve[Volume::VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[Volume::VOLMAX].mIndex) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = curve[segment].mDBAttenuation + + ((float)(volIdx - curve[segment].mIndex)) * + ( (curve[segment+1].mDBAttenuation - + curve[segment].mDBAttenuation) / + ((float)(curve[segment+1].mIndex - + curve[segment].mIndex)) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + curve[segment].mIndex, volIdx, + curve[segment+1].mIndex, + curve[segment].mDBAttenuation, + decibels, + curve[segment+1].mDBAttenuation, + amplification); + + return amplification; +} + +}; // namespace android diff --git a/services/audiopolicy/enginedefault/src/Gains.h b/services/audiopolicy/enginedefault/src/Gains.h new file mode 100644 index 0000000..b5601ca --- /dev/null +++ b/services/audiopolicy/enginedefault/src/Gains.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <StreamDescriptor.h> +#include <utils/KeyedVector.h> +#include <system/audio.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +namespace android { + +class StreamDescriptor; + +class Gains +{ +public : + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + + // default volume curve + static const VolumeCurvePoint sDefaultVolumeCurve[Volume::VOLCNT]; + // default volume curve for media strategy + static const VolumeCurvePoint sDefaultMediaVolumeCurve[Volume::VOLCNT]; + // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) + static const VolumeCurvePoint sExtMediaSystemVolumeCurve[Volume::VOLCNT]; + // volume curve for media strategy on speakers + static const VolumeCurvePoint sSpeakerMediaVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[Volume::VOLCNT]; + // volume curve for sonification strategy on speakers + static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[Volume::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[Volume::VOLCNT]; + static const VolumeCurvePoint sHeadsetSystemVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sDefaultVoiceVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sLinearVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sSilentVolumeCurve[Volume::VOLCNT]; + static const VolumeCurvePoint sFullScaleVolumeCurve[Volume::VOLCNT]; + // default volume curves per stream and device category. See initializeVolumeCurves() + static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][Volume::DEVICE_CATEGORY_CNT]; +}; + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyFactory.cpp b/services/audiopolicy/manager/AudioPolicyFactory.cpp index 2ae7bc1..9910a1f 100644 --- a/services/audiopolicy/AudioPolicyFactory.cpp +++ b/services/audiopolicy/manager/AudioPolicyFactory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "AudioPolicyManager.h" +#include "managerdefault/AudioPolicyManager.h" namespace android { diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp index 6ebd0ed..797a2b4 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "AudioPolicyManager" +#define LOG_TAG "APM::AudioPolicyManager" //#define LOG_NDEBUG 0 //#define VERY_VERBOSE_LOGGING @@ -24,20 +24,11 @@ #define ALOGVV(a...) do { } while(0) #endif -// A device mask for all audio input devices that are considered "virtual" when evaluating -// active inputs in getActiveInput() -#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX|AUDIO_DEVICE_IN_FM_TUNER) -// A device mask for all audio output devices that are considered "remote" when evaluating -// active output devices in isStreamActiveRemotely() -#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX -// A device mask for all audio input and output devices where matching inputs/outputs on device -// type alone is not enough: the address must match too -#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \ - AUDIO_DEVICE_OUT_REMOTE_SUBMIX) - #include <inttypes.h> #include <math.h> +#include <AudioPolicyManagerInterface.h> +#include <AudioPolicyEngineInstance.h> #include <cutils/properties.h> #include <utils/Log.h> #include <hardware/audio.h> @@ -47,188 +38,36 @@ #include <soundtrigger/SoundTrigger.h> #include "AudioPolicyManager.h" #include "audio_policy_conf.h" +#include <ConfigParsingUtils.h> +#include <policy.h> namespace android { // ---------------------------------------------------------------------------- -// Definitions for audio_policy.conf file parsing -// ---------------------------------------------------------------------------- - -struct StringToEnum { - const char *name; - uint32_t value; -}; - -#define STRING_TO_ENUM(string) { #string, string } -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -const StringToEnum sDeviceNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_SCO), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_TELEPHONY_TX), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), - STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), - STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), - STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_IN_ALL_SCO), - STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), - STRING_TO_ENUM(AUDIO_DEVICE_IN_HDMI), - STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), - STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), - STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC), - STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX), - STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), - STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY), - STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER), - STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), - STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), - STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), - STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), - STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK), -}; - -const StringToEnum sOutputFlagNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), - STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), -}; - -const StringToEnum sInputFlagNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_INPUT_FLAG_FAST), - STRING_TO_ENUM(AUDIO_INPUT_FLAG_HW_HOTWORD), -}; - -const StringToEnum sFormatNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), - STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), - STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT), - STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT), - STRING_TO_ENUM(AUDIO_FORMAT_PCM_FLOAT), - STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED), - STRING_TO_ENUM(AUDIO_FORMAT_MP3), - STRING_TO_ENUM(AUDIO_FORMAT_AAC), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_MAIN), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LC), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_SSR), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LTP), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V1), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_SCALABLE), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_ERLC), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_LD), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V2), - STRING_TO_ENUM(AUDIO_FORMAT_AAC_ELD), - STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), - STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), - STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), - STRING_TO_ENUM(AUDIO_FORMAT_OPUS), - STRING_TO_ENUM(AUDIO_FORMAT_AC3), - STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), -}; - -const StringToEnum sOutChannelsNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), - STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), -}; - -const StringToEnum sInChannelsNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), - STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), - STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK), -}; - -const StringToEnum sGainModeNameToEnumTable[] = { - STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT), - STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS), - STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP), -}; - - -uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table, - size_t size, - const char *name) -{ - for (size_t i = 0; i < size; i++) { - if (strcmp(table[i].name, name) == 0) { - ALOGV("stringToEnum() found %s", table[i].name); - return table[i].value; - } - } - return 0; -} - -const char *AudioPolicyManager::enumToString(const struct StringToEnum *table, - size_t size, - uint32_t value) -{ - for (size_t i = 0; i < size; i++) { - if (table[i].value == value) { - return table[i].name; - } - } - return ""; -} - -bool AudioPolicyManager::stringToBool(const char *value) -{ - return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); -} - - -// ---------------------------------------------------------------------------- // AudioPolicyInterface implementation // ---------------------------------------------------------------------------- status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const char *device_address) + audio_policy_dev_state_t state, + const char *device_address, + const char *device_name) { - return setDeviceConnectionStateInt(device, state, device_address); + return setDeviceConnectionStateInt(device, state, device_address, device_name); } status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { - ALOGV("setDeviceConnectionState() device: %x, state %d, address %s", - device, state, device_address != NULL ? device_address : ""); + ALOGV("setDeviceConnectionStateInt() device: 0x%X, state %d, address %s name %s", +- device, state, device_address, device_name); // connect/disconnect only 1 device at a time if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; - sp<DeviceDescriptor> devDesc = getDeviceDescriptor(device, device_address); + sp<DeviceDescriptor> devDesc = + mHwModules.getDeviceDescriptor(device, device_address, device_name); // handle output devices if (audio_is_output_device(device)) { @@ -252,15 +91,14 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, // register new device as available index = mAvailableOutputDevices.add(devDesc); if (index >= 0) { - sp<HwModule> module = getModuleForDevice(device); + sp<HwModule> module = mHwModules.getModuleForDevice(device); if (module == 0) { ALOGD("setDeviceConnectionState() could not find HW module for device %08x", device); mAvailableOutputDevices.remove(devDesc); return INVALID_OPERATION; } - mAvailableOutputDevices[index]->mId = nextUniqueId(); - mAvailableOutputDevices[index]->mModule = module; + mAvailableOutputDevices[index]->attach(module); } else { return NO_MEMORY; } @@ -269,14 +107,16 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, mAvailableOutputDevices.remove(devDesc); return INVALID_OPERATION; } + // Propagate device availability to Engine + mEngine->setDeviceConnectionState(devDesc, state); + // outputs should never be empty here ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():" "checkOutputsForDevice() returned no outputs but status OK"); ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs", outputs.size()); - - // Set connect to HALs + // Send connect to HALs AudioParameter param = AudioParameter(devDesc->mAddress); param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); @@ -291,7 +131,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, ALOGV("setDeviceConnectionState() disconnecting output device %x", device); - // Set Disconnect to HALs + // Send Disconnect to HALs AudioParameter param = AudioParameter(devDesc->mAddress); param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); @@ -300,6 +140,9 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, mAvailableOutputDevices.remove(devDesc); checkOutputsForDevice(devDesc, state, outputs, devDesc->mAddress); + + // Propagate device availability to Engine + mEngine->setDeviceConnectionState(devDesc, state); } break; default: @@ -328,20 +171,20 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, } updateDevicesAndOutputs(); - if (mPhoneState == AUDIO_MODE_IN_CALL) { + if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) { audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); updateCallRouting(newDevice); } for (size_t i = 0; i < mOutputs.size(); i++) { audio_io_handle_t output = mOutputs.keyAt(i); - if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i), true /*fromCache*/); // do not force device change on duplicated output because if device is 0, it will // also force a device 0 for the two outputs it is duplicated to which may override // a valid device selection on those outputs. bool force = !mOutputs.valueAt(i)->isDuplicated() - && (!deviceDistinguishesOnAddress(device) + && (!device_distinguishes_on_address(device) // always force when disconnecting (a non-duplicated device) || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)); setOutputDevice(output, newDevice, force, 0); @@ -365,7 +208,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, ALOGW("setDeviceConnectionState() device already connected: %d", device); return INVALID_OPERATION; } - sp<HwModule> module = getModuleForDevice(device); + sp<HwModule> module = mHwModules.getModuleForDevice(device); if (module == NULL) { ALOGW("setDeviceConnectionState(): could not find HW module for device %08x", device); @@ -377,8 +220,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, index = mAvailableInputDevices.add(devDesc); if (index >= 0) { - mAvailableInputDevices[index]->mId = nextUniqueId(); - mAvailableInputDevices[index]->mModule = module; + mAvailableInputDevices[index]->attach(module); } else { return NO_MEMORY; } @@ -388,6 +230,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, param.addInt(String8(AUDIO_PARAMETER_DEVICE_CONNECT), device); mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + // Propagate device availability to Engine + mEngine->setDeviceConnectionState(devDesc, state); } break; // handle input device disconnection @@ -407,6 +251,8 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, checkInputsForDevice(device, state, inputs, devDesc->mAddress); mAvailableInputDevices.remove(devDesc); + // Propagate device availability to Engine + mEngine->setDeviceConnectionState(devDesc, state); } break; default: @@ -416,7 +262,7 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, closeAllInputs(); - if (mPhoneState == AUDIO_MODE_IN_CALL) { + if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) { audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); updateCallRouting(newDevice); } @@ -430,9 +276,10 @@ status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t device, } audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devices_t device, - const char *device_address) + const char *device_address) { - sp<DeviceDescriptor> devDesc = getDeviceDescriptor(device, device_address); + sp<DeviceDescriptor> devDesc = mHwModules.getDeviceDescriptor(device, device_address, ""); + DeviceVector *deviceVector; if (audio_is_output_device(device)) { @@ -443,43 +290,7 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi ALOGW("getDeviceConnectionState() invalid device type %08x", device); return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; } - - ssize_t index = deviceVector->indexOf(devDesc); - if (index >= 0) { - return AUDIO_POLICY_DEVICE_STATE_AVAILABLE; - } else { - return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; - } -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::getDeviceDescriptor( - const audio_devices_t device, - const char *device_address) -{ - String8 address = (device_address == NULL) ? String8("") : String8(device_address); - // handle legacy remote submix case where the address was not always specified - if (deviceDistinguishesOnAddress(device) && (address.length() == 0)) { - address = String8("0"); - } - - for (size_t i = 0; i < mHwModules.size(); i++) { - if (mHwModules[i]->mHandle == 0) { - continue; - } - DeviceVector deviceList = - mHwModules[i]->mDeclaredDevices.getDevicesFromTypeAddr(device, address); - if (!deviceList.isEmpty()) { - return deviceList.itemAt(0); - } - deviceList = mHwModules[i]->mDeclaredDevices.getDevicesFromType(device); - if (!deviceList.isEmpty()) { - return deviceList.itemAt(0); - } - } - - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = address; - return devDesc; + return deviceVector->getDeviceConnectionState(devDesc); } void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs) @@ -550,8 +361,7 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs ALOGW_IF(status != NO_ERROR, "updateCallRouting() error %d creating RX audio patch", status); if (status == NO_ERROR) { - mCallRxPatch = new AudioPatch((audio_patch_handle_t)nextUniqueId(), - &patch, mUidCached); + mCallRxPatch = new AudioPatch(&patch, mUidCached); mCallRxPatch->mAfPatchHandle = afPatchHandle; mCallRxPatch->mUid = mUidCached; } @@ -593,8 +403,7 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs ALOGW_IF(status != NO_ERROR, "setPhoneState() error %d creating TX audio patch", status); if (status == NO_ERROR) { - mCallTxPatch = new AudioPatch((audio_patch_handle_t)nextUniqueId(), - &patch, mUidCached); + mCallTxPatch = new AudioPatch(&patch, mUidCached); mCallTxPatch->mAfPatchHandle = afPatchHandle; mCallTxPatch->mUid = mUidCached; } @@ -604,16 +413,14 @@ void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs void AudioPolicyManager::setPhoneState(audio_mode_t state) { ALOGV("setPhoneState() state %d", state); - if (state < 0 || state >= AUDIO_MODE_CNT) { - ALOGW("setPhoneState() invalid state %d", state); - return; - } + // store previous phone state for management of sonification strategy below + int oldState = mEngine->getPhoneState(); - if (state == mPhoneState ) { - ALOGW("setPhoneState() setting same state %d", state); + if (mEngine->setPhoneState(state) != NO_ERROR) { + ALOGW("setPhoneState() invalid or same state %d", state); return; } - + /// Opens: can these line be executed after the switch of volume curves??? // if leaving call state, handle special case of active streams // pertaining to sonification strategy see handleIncallSonification() if (isInCall()) { @@ -629,36 +436,12 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY); } - // store previous phone state for management of sonification strategy below - int oldState = mPhoneState; - mPhoneState = state; - bool force = false; - - // are we entering or starting a call - if (!isStateInCall(oldState) && isStateInCall(state)) { - ALOGV(" Entering call in setPhoneState()"); - // force routing command to audio hardware when starting a call - // even if no device change is needed - force = true; - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { - mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = - sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; - } - } else if (isStateInCall(oldState) && !isStateInCall(state)) { - ALOGV(" Exiting call in setPhoneState()"); - // force routing command to audio hardware when exiting a call - // even if no device change is needed - force = true; - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { - mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = - sVolumeProfiles[AUDIO_STREAM_DTMF][j]; - } - } else if (isStateInCall(state) && (state != oldState)) { - ALOGV(" Switching between telephony and VoIP in setPhoneState()"); - // force routing command to audio hardware when switching between telephony and VoIP - // even if no device change is needed - force = true; - } + /** + * Switching to or from incall state or switching between telephony and VoIP lead to force + * routing command. + */ + bool force = ((is_state_in_call(oldState) != is_state_in_call(state)) + || (is_state_in_call(state) && (state != oldState))); // check for device and output changes triggered by new phone state checkA2dpSuspend(); @@ -675,12 +458,12 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) // mute media and sonification strategies and delay device switch by the largest // latency of any output where either strategy is active. // This avoid sending the ring tone or music tail into the earpiece or headset. - if ((desc->isStrategyActive(STRATEGY_MEDIA, - SONIFICATION_HEADSET_MUSIC_DELAY, - sysTime) || - desc->isStrategyActive(STRATEGY_SONIFICATION, - SONIFICATION_HEADSET_MUSIC_DELAY, - sysTime)) && + if ((isStrategyActive(desc, STRATEGY_MEDIA, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime) || + isStrategyActive(desc, STRATEGY_SONIFICATION, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && (delayMs < (int)desc->mLatency*2)) { delayMs = desc->mLatency*2; } @@ -738,84 +521,35 @@ void AudioPolicyManager::setPhoneState(audio_mode_t state) } } +audio_mode_t AudioPolicyManager::getPhoneState() { + return mEngine->getPhoneState(); +} + void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) { - ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); - - bool forceVolumeReeval = false; - switch(usage) { - case AUDIO_POLICY_FORCE_FOR_COMMUNICATION: - if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO && - config != AUDIO_POLICY_FORCE_NONE) { - ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); - return; - } - forceVolumeReeval = true; - mForceUse[usage] = config; - break; - case AUDIO_POLICY_FORCE_FOR_MEDIA: - if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && - config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && - config != AUDIO_POLICY_FORCE_ANALOG_DOCK && - config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE && - config != AUDIO_POLICY_FORCE_NO_BT_A2DP && config != AUDIO_POLICY_FORCE_SPEAKER ) { - ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); - return; - } - mForceUse[usage] = config; - break; - case AUDIO_POLICY_FORCE_FOR_RECORD: - if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && - config != AUDIO_POLICY_FORCE_NONE) { - ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); - return; - } - mForceUse[usage] = config; - break; - case AUDIO_POLICY_FORCE_FOR_DOCK: - if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && - config != AUDIO_POLICY_FORCE_BT_DESK_DOCK && - config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && - config != AUDIO_POLICY_FORCE_ANALOG_DOCK && - config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { - ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); - } - forceVolumeReeval = true; - mForceUse[usage] = config; - break; - case AUDIO_POLICY_FORCE_FOR_SYSTEM: - if (config != AUDIO_POLICY_FORCE_NONE && - config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { - ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); - } - forceVolumeReeval = true; - mForceUse[usage] = config; - break; - case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO: - if (config != AUDIO_POLICY_FORCE_NONE && - config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) { - ALOGW("setForceUse() invalid config %d forHDMI_SYSTEM_AUDIO", config); - } - mForceUse[usage] = config; - break; - default: - ALOGW("setForceUse() invalid usage %d", usage); - break; + ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mEngine->getPhoneState()); + + if (mEngine->setForceUse(usage, config) != NO_ERROR) { + ALOGW("setForceUse() could not set force cfg %d for usage %d", config, usage); + return; } + bool forceVolumeReeval = (usage == AUDIO_POLICY_FORCE_FOR_COMMUNICATION) || + (usage == AUDIO_POLICY_FORCE_FOR_DOCK) || + (usage == AUDIO_POLICY_FORCE_FOR_SYSTEM); // check for device and output changes triggered by new force usage checkA2dpSuspend(); checkOutputForAllStrategies(); updateDevicesAndOutputs(); - if (mPhoneState == AUDIO_MODE_IN_CALL) { + if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) { audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/); updateCallRouting(newDevice); } for (size_t i = 0; i < mOutputs.size(); i++) { audio_io_handle_t output = mOutputs.keyAt(i); audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/); - if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); } if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { @@ -823,18 +557,13 @@ void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, } } - audio_io_handle_t activeInput = getActiveInput(); + audio_io_handle_t activeInput = mInputs.getActiveInput(); if (activeInput != 0) { setInputDevice(activeInput, getNewInputDevice(activeInput)); } } -audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage) -{ - return mForceUse[usage]; -} - void AudioPolicyManager::setSystemProperty(const char* property, const char* value) { ALOGV("setSystemProperty() property %s, value %s", property, value); @@ -842,7 +571,7 @@ void AudioPolicyManager::setSystemProperty(const char* property, const char* val // Find a direct output profile compatible with the parameters passed, even if the input flags do // not explicitly request a direct output -sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput( +sp<IOProfile> AudioPolicyManager::getProfileForDirectOutput( audio_devices_t device, uint32_t samplingRate, audio_format_t format, @@ -868,11 +597,11 @@ sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput( } audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) { routing_strategy strategy = getStrategy(stream); audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); @@ -910,45 +639,16 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } stream_type_to_audio_attributes(*stream, &attributes); } - - for (size_t i = 0; i < mPolicyMixes.size(); i++) { - sp<AudioOutputDescriptor> desc; - if (mPolicyMixes[i]->mMix.mMixType == MIX_TYPE_PLAYERS) { - for (size_t j = 0; j < mPolicyMixes[i]->mMix.mCriteria.size(); j++) { - if ((RULE_MATCH_ATTRIBUTE_USAGE == mPolicyMixes[i]->mMix.mCriteria[j].mRule && - mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mUsage == attributes.usage) || - (RULE_EXCLUDE_ATTRIBUTE_USAGE == mPolicyMixes[i]->mMix.mCriteria[j].mRule && - mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mUsage != attributes.usage)) { - desc = mPolicyMixes[i]->mOutput; - break; - } - if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && - strncmp(attributes.tags + strlen("addr="), - mPolicyMixes[i]->mMix.mRegistrationId.string(), - AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = mPolicyMixes[i]->mOutput; - break; - } - } - } else if (mPolicyMixes[i]->mMix.mMixType == MIX_TYPE_RECORDERS) { - if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE && - strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 && - strncmp(attributes.tags + strlen("addr="), - mPolicyMixes[i]->mMix.mRegistrationId.string(), - AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) { - desc = mPolicyMixes[i]->mOutput; - } - } - if (desc != 0) { - if (!audio_is_linear_pcm(format)) { - return BAD_VALUE; - } - desc->mPolicyMix = &mPolicyMixes[i]->mMix; - *stream = streamTypefromAttributesInt(&attributes); - *output = desc->mIoHandle; - ALOGV("getOutputForAttr() returns output %d", *output); - return NO_ERROR; + sp<AudioOutputDescriptor> desc; + if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) { + ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr"); + if (!audio_is_linear_pcm(format)) { + return BAD_VALUE; } + *stream = streamTypefromAttributesInt(&attributes); + *output = desc->mIoHandle; + ALOGV("getOutputForAttr() returns output %d", *output); + return NO_ERROR; } if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) { ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE"); @@ -956,7 +656,7 @@ status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, } ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x", - attributes.usage, attributes.content_type, attributes.tags, attributes.flags); + attributes.usage, attributes.content_type, attributes.tags, attributes.flags); routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes); audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); @@ -1066,7 +766,7 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( // in the background. if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || - !isNonOffloadableEffectEnabled()) { + !mEffects.isNonOffloadableEffectEnabled()) { profile = getProfileForDirectOutput(device, samplingRate, format, @@ -1130,6 +830,10 @@ audio_io_handle_t AudioPolicyManager::getOutputForDevice( if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) { goto non_direct_output; } + // fall back to mixer output if possible when the direct output could not be open + if (audio_is_linear_pcm(format) && samplingRate <= MAX_MIXER_SAMPLING_RATE) { + goto non_direct_output; + } return AUDIO_IO_HANDLE_NONE; } outputDesc->mSamplingRate = config.sample_rate; @@ -1248,7 +952,7 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, uint32_t beaconMuteLatency = 0; if (stream == AUDIO_STREAM_TTS) { ALOGV("\t found BEACON stream"); - if (isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { + if (mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { return INVALID_OPERATION; } else { beaconMuteLatency = handleEventForBeacon(STARTING_BEACON); @@ -1322,7 +1026,8 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - outputDesc->mPolicyMix->mRegistrationId); + outputDesc->mPolicyMix->mRegistrationId, + "remote-submix"); } // force reevaluating accessibility routing when ringtone or alarm starts @@ -1371,7 +1076,8 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - outputDesc->mPolicyMix->mRegistrationId); + outputDesc->mPolicyMix->mRegistrationId, + "remote-submix"); } outputDesc->mStopTime[stream] = systemTime(); @@ -1425,7 +1131,7 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); if (outputDesc->isActive()) { mpClientInterface->closeOutput(output); - mOutputs.removeItem(output); + removeOutput(output); mTestOutputs[testIndex] = 0; } return; @@ -1483,19 +1189,13 @@ status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr, if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX && strncmp(attr->tags, "addr=", strlen("addr=")) == 0) { - device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; - address = String8(attr->tags + strlen("addr=")); - ssize_t index = mPolicyMixes.indexOfKey(address); - if (index < 0) { - ALOGW("getInputForAttr() no policy for address %s", address.string()); - return BAD_VALUE; - } - if (mPolicyMixes[index]->mMix.mMixType != MIX_TYPE_PLAYERS) { - ALOGW("getInputForAttr() bad policy mix type for address %s", address.string()); - return BAD_VALUE; + status_t ret = mPolicyMixes.getInputMixForAttr(*attr, policyMix); + if (ret != NO_ERROR) { + return ret; } - policyMix = &mPolicyMixes[index]->mMix; *inputType = API_INPUT_MIX_EXT_POLICY_REROUTE; + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + address = String8(attr->tags + strlen("addr=")); } else { device = getDeviceAndMixForInputSource(inputSource, &policyMix); if (device == AUDIO_DEVICE_NONE) { @@ -1633,10 +1333,10 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } // virtual input devices are compatible with other input devices - if (!isVirtualInputDevice(inputDesc->mDevice)) { + if (!is_virtual_input_device(inputDesc->mDevice)) { // for a non-virtual input device, check if there is another (non-virtual) active input - audio_io_handle_t activeInput = getActiveInput(); + audio_io_handle_t activeInput = mInputs.getActiveInput(); if (activeInput != 0 && activeInput != input) { // If the already active input uses AUDIO_SOURCE_HOTWORD then it is closed, @@ -1654,7 +1354,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, } if (inputDesc->mRefCount == 0) { - if (activeInputsCount() == 0) { + if (mInputs.activeInputsCount() == 0) { SoundTrigger::setCaptureState(true); } setInputDevice(input, getNewInputDevice(input), true /* force */); @@ -1672,7 +1372,7 @@ status_t AudioPolicyManager::startInput(audio_io_handle_t input, if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address); + address, "remote-submix"); } } } @@ -1720,13 +1420,13 @@ status_t AudioPolicyManager::stopInput(audio_io_handle_t input, if (address != "") { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address); + address, "remote-submix"); } } resetInputDevice(input); - if (activeInputsCount() == 0) { + if (mInputs.activeInputsCount() == 0) { SoundTrigger::setCaptureState(false); } } @@ -1793,25 +1493,20 @@ void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, int indexMax) { ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); - if (indexMin < 0 || indexMin >= indexMax) { - ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); - return; - } - mStreams[stream].mIndexMin = indexMin; - mStreams[stream].mIndexMax = indexMax; + mEngine->initStreamVolume(stream, indexMin, indexMax); //FIXME: AUDIO_STREAM_ACCESSIBILITY volume follows AUDIO_STREAM_MUSIC for now if (stream == AUDIO_STREAM_MUSIC) { - mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexMin = indexMin; - mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexMax = indexMax; + mEngine->initStreamVolume(AUDIO_STREAM_ACCESSIBILITY, indexMin, indexMax); } } status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, - int index, - audio_devices_t device) + int index, + audio_devices_t device) { - if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + if ((index < mStreams[stream].getVolumeIndexMin()) || + (index > mStreams[stream].getVolumeIndexMax())) { return BAD_VALUE; } if (!audio_is_output_device(device)) { @@ -1819,7 +1514,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, } // Force max volume if stream cannot be muted - if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + if (!mStreams.canBeMuted(stream)) index = mStreams[stream].getVolumeIndexMax(); ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d", stream, device, index); @@ -1827,9 +1522,9 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and // clear all device specific values if (device == AUDIO_DEVICE_OUT_DEFAULT) { - mStreams[stream].mIndexCur.clear(); + mStreams.clearCurrentVolumeIndex(stream); } - mStreams[stream].mIndexCur.add(device, index); + mStreams.addCurrentVolumeIndex(stream, device, index); // update volume on all outputs whose current device is also selected by the same // strategy as the device specified by the caller @@ -1839,7 +1534,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, //FIXME: AUDIO_STREAM_ACCESSIBILITY volume follows AUDIO_STREAM_MUSIC for now audio_devices_t accessibilityDevice = AUDIO_DEVICE_NONE; if (stream == AUDIO_STREAM_MUSIC) { - mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexCur.add(device, index); + mStreams.addCurrentVolumeIndex(AUDIO_STREAM_ACCESSIBILITY, device, index); accessibilityDevice = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, true /*fromCache*/); } if ((device != AUDIO_DEVICE_OUT_DEFAULT) && @@ -1848,8 +1543,7 @@ status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, } status_t status = NO_ERROR; for (size_t i = 0; i < mOutputs.size(); i++) { - audio_devices_t curDevice = - getDeviceForVolume(mOutputs.valueAt(i)->device()); + audio_devices_t curDevice = Volume::getDeviceForVolume(mOutputs.valueAt(i)->device()); if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) { status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); if (volStatus != NO_ERROR) { @@ -1879,7 +1573,7 @@ status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, if (device == AUDIO_DEVICE_OUT_DEFAULT) { device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); } - device = getDeviceForVolume(device); + device = Volume::getDeviceForVolume(device); *index = mStreams[stream].getVolumeIndex(device); ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); @@ -1956,137 +1650,7 @@ status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, return INVALID_OPERATION; } } - - if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { - ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", - desc->name, desc->memoryUsage); - return INVALID_OPERATION; - } - mTotalEffectsMemory += desc->memoryUsage; - ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", - desc->name, io, strategy, session, id); - ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); - - sp<EffectDescriptor> effectDesc = new EffectDescriptor(); - memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); - effectDesc->mIo = io; - effectDesc->mStrategy = (routing_strategy)strategy; - effectDesc->mSession = session; - effectDesc->mEnabled = false; - - mEffects.add(id, effectDesc); - - return NO_ERROR; -} - -status_t AudioPolicyManager::unregisterEffect(int id) -{ - ssize_t index = mEffects.indexOfKey(id); - if (index < 0) { - ALOGW("unregisterEffect() unknown effect ID %d", id); - return INVALID_OPERATION; - } - - sp<EffectDescriptor> effectDesc = mEffects.valueAt(index); - - setEffectEnabled(effectDesc, false); - - if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) { - ALOGW("unregisterEffect() memory %d too big for total %d", - effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); - effectDesc->mDesc.memoryUsage = mTotalEffectsMemory; - } - mTotalEffectsMemory -= effectDesc->mDesc.memoryUsage; - ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", - effectDesc->mDesc.name, id, effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); - - mEffects.removeItem(id); - - return NO_ERROR; -} - -status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) -{ - ssize_t index = mEffects.indexOfKey(id); - if (index < 0) { - ALOGW("unregisterEffect() unknown effect ID %d", id); - return INVALID_OPERATION; - } - - return setEffectEnabled(mEffects.valueAt(index), enabled); -} - -status_t AudioPolicyManager::setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled) -{ - if (enabled == effectDesc->mEnabled) { - ALOGV("setEffectEnabled(%s) effect already %s", - enabled?"true":"false", enabled?"enabled":"disabled"); - return INVALID_OPERATION; - } - - if (enabled) { - if (mTotalEffectsCpuLoad + effectDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { - ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", - effectDesc->mDesc.name, (float)effectDesc->mDesc.cpuLoad/10); - return INVALID_OPERATION; - } - mTotalEffectsCpuLoad += effectDesc->mDesc.cpuLoad; - ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); - } else { - if (mTotalEffectsCpuLoad < effectDesc->mDesc.cpuLoad) { - ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", - effectDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); - effectDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; - } - mTotalEffectsCpuLoad -= effectDesc->mDesc.cpuLoad; - ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); - } - effectDesc->mEnabled = enabled; - return NO_ERROR; -} - -bool AudioPolicyManager::isNonOffloadableEffectEnabled() -{ - for (size_t i = 0; i < mEffects.size(); i++) { - sp<EffectDescriptor> effectDesc = mEffects.valueAt(i); - if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) && - ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { - ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", - effectDesc->mDesc.name, effectDesc->mSession); - return true; - } - } - return false; -} - -bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const -{ - nsecs_t sysTime = systemTime(); - for (size_t i = 0; i < mOutputs.size(); i++) { - const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); - if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { - return true; - } - } - return false; -} - -bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, - uint32_t inPastMs) const -{ - nsecs_t sysTime = systemTime(); - for (size_t i = 0; i < mOutputs.size(); i++) { - const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); - if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && - outputDesc->isStreamActive(stream, inPastMs, sysTime)) { - // do not consider re routing (when the output is going to a dynamic policy) - // as "remote playback" - if (outputDesc->mPolicyMix == NULL) { - return true; - } - } - } - return false; + return mEffects.registerEffect(desc, io, strategy, session, id); } bool AudioPolicyManager::isSourceActive(audio_source_t source) const @@ -2156,9 +1720,8 @@ status_t AudioPolicyManager::registerPolicyMixes(Vector<AudioMix> mixes) for (size_t i = 0; i < mixes.size(); i++) { String8 address = mixes[i].mRegistrationId; - ssize_t index = mPolicyMixes.indexOfKey(address); - if (index >= 0) { - ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string()); + + if (mPolicyMixes.registerMix(address, mixes[i]) != NO_ERROR) { continue; } audio_config_t outputConfig = mixes[i].mFormat; @@ -2171,17 +1734,15 @@ status_t AudioPolicyManager::registerPolicyMixes(Vector<AudioMix> mixes) AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address); module->addInputProfile(address, &inputConfig, AUDIO_DEVICE_IN_REMOTE_SUBMIX, address); - sp<AudioPolicyMix> policyMix = new AudioPolicyMix(); - policyMix->mMix = mixes[i]; - mPolicyMixes.add(address, policyMix); + if (mixes[i].mMixType == MIX_TYPE_PLAYERS) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string()); + address.string(), "remote-submix"); } else { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_AVAILABLE, - address.string()); + address.string(), "remote-submix"); } } return NO_ERROR; @@ -2206,20 +1767,17 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector<AudioMix> mixes) for (size_t i = 0; i < mixes.size(); i++) { String8 address = mixes[i].mRegistrationId; - ssize_t index = mPolicyMixes.indexOfKey(address); - if (index < 0) { - ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string()); + + if (mPolicyMixes.unregisterMix(address) != NO_ERROR) { continue; } - mPolicyMixes.removeItemsAt(index); - if (getDeviceConnectionState(AUDIO_DEVICE_IN_REMOTE_SUBMIX, address.string()) == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string()); + address.string(), "remote-submix"); } if (getDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address.string()) == @@ -2227,7 +1785,7 @@ status_t AudioPolicyManager::unregisterPolicyMixes(Vector<AudioMix> mixes) { setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, - address.string()); + address.string(), "remote-submix"); } module->removeOutputProfile(address); module->removeInputProfile(address); @@ -2247,87 +1805,32 @@ status_t AudioPolicyManager::dump(int fd) snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput); result.append(buffer); - snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); + snprintf(buffer, SIZE, " Phone state: %d\n", mEngine->getPhoneState()); result.append(buffer); snprintf(buffer, SIZE, " Force use for communications %d\n", - mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION)); result.append(buffer); - snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]); + snprintf(buffer, SIZE, " Force use for media %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA)); result.append(buffer); - snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD]); + snprintf(buffer, SIZE, " Force use for record %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD)); result.append(buffer); - snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK]); + snprintf(buffer, SIZE, " Force use for dock %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK)); result.append(buffer); - snprintf(buffer, SIZE, " Force use for system %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM]); + snprintf(buffer, SIZE, " Force use for system %d\n", mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM)); result.append(buffer); snprintf(buffer, SIZE, " Force use for hdmi system audio %d\n", - mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO]); - result.append(buffer); - - snprintf(buffer, SIZE, " Available output devices:\n"); + mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO)); result.append(buffer); write(fd, result.string(), result.size()); - for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { - mAvailableOutputDevices[i]->dump(fd, 2, i); - } - snprintf(buffer, SIZE, "\n Available input devices:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { - mAvailableInputDevices[i]->dump(fd, 2, i); - } - - snprintf(buffer, SIZE, "\nHW Modules dump:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mHwModules.size(); i++) { - snprintf(buffer, SIZE, "- HW Module %zu:\n", i + 1); - write(fd, buffer, strlen(buffer)); - mHwModules[i]->dump(fd); - } - - snprintf(buffer, SIZE, "\nOutputs dump:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mOutputs.size(); i++) { - snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i)); - write(fd, buffer, strlen(buffer)); - mOutputs.valueAt(i)->dump(fd); - } - - snprintf(buffer, SIZE, "\nInputs dump:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mInputs.size(); i++) { - snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i)); - write(fd, buffer, strlen(buffer)); - mInputs.valueAt(i)->dump(fd); - } - snprintf(buffer, SIZE, "\nStreams dump:\n"); - write(fd, buffer, strlen(buffer)); - snprintf(buffer, SIZE, - " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < AUDIO_STREAM_CNT; i++) { - snprintf(buffer, SIZE, " %02zu ", i); - write(fd, buffer, strlen(buffer)); - mStreams[i].dump(fd); - } - - snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", - (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); - write(fd, buffer, strlen(buffer)); - - snprintf(buffer, SIZE, "Registered effects:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mEffects.size(); i++) { - snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i)); - write(fd, buffer, strlen(buffer)); - mEffects.valueAt(i)->dump(fd); - } - - snprintf(buffer, SIZE, "\nAudio Patches:\n"); - write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mAudioPatches.size(); i++) { - mAudioPatches[i]->dump(fd, 2, i); - } + mAvailableOutputDevices.dump(fd, String8("output")); + mAvailableInputDevices.dump(fd, String8("input")); + mHwModules.dump(fd); + mOutputs.dump(fd); + mInputs.dump(fd); + mStreams.dump(fd); + mEffects.dump(fd); + mAudioPatches.dump(fd); return NO_ERROR; } @@ -2384,7 +1887,7 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI // FIXME: We should check the audio session here but we do not have it in this context. // This may prevent offloading in rare situations where effects are left active by apps // in the background. - if (isNonOffloadableEffectEnabled()) { + if (mEffects.isNonOffloadableEffectEnabled()) { return false; } @@ -2463,93 +1966,6 @@ status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused) return NO_ERROR; } -sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromId( - audio_port_handle_t id) const -{ - sp<AudioOutputDescriptor> outputDesc = NULL; - for (size_t i = 0; i < mOutputs.size(); i++) { - outputDesc = mOutputs.valueAt(i); - if (outputDesc->mId == id) { - break; - } - } - return outputDesc; -} - -sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId( - audio_port_handle_t id) const -{ - sp<AudioInputDescriptor> inputDesc = NULL; - for (size_t i = 0; i < mInputs.size(); i++) { - inputDesc = mInputs.valueAt(i); - if (inputDesc->mId == id) { - break; - } - } - return inputDesc; -} - -sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice( - audio_devices_t device) const -{ - sp <HwModule> module; - - for (size_t i = 0; i < mHwModules.size(); i++) { - if (mHwModules[i]->mHandle == 0) { - continue; - } - if (audio_is_output_device(device)) { - for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) - { - if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) { - return mHwModules[i]; - } - } - } else { - for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) { - if (mHwModules[i]->mInputProfiles[j]->mSupportedDevices.types() & - device & ~AUDIO_DEVICE_BIT_IN) { - return mHwModules[i]; - } - } - } - } - return module; -} - -sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleFromName(const char *name) const -{ - sp <HwModule> module; - - for (size_t i = 0; i < mHwModules.size(); i++) - { - if (strcmp(mHwModules[i]->mName, name) == 0) { - return mHwModules[i]; - } - } - return module; -} - -audio_devices_t AudioPolicyManager::availablePrimaryOutputDevices() -{ - sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); - audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types(); - return devices & mAvailableOutputDevices.types(); -} - -audio_devices_t AudioPolicyManager::availablePrimaryInputDevices() -{ - audio_module_handle_t primaryHandle = - mOutputs.valueFor(mPrimaryOutput)->mProfile->mModule->mHandle; - audio_devices_t devices = AUDIO_DEVICE_NONE; - for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { - if (mAvailableInputDevices[i]->mModule->mHandle == primaryHandle) { - devices |= mAvailableInputDevices[i]->mDeviceType; - } - } - return devices; -} - status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, audio_patch_handle_t *handle, uid_t uid) @@ -2605,7 +2021,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) { - sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id); + sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id); if (outputDesc == NULL) { ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id); return BAD_VALUE; @@ -2634,15 +2050,14 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } - if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType, + if (!outputDesc->mProfile->isCompatibleProfile(devDesc->type(), devDesc->mAddress, patch->sources[0].sample_rate, - NULL, // updatedSamplingRate - patch->sources[0].format, - patch->sources[0].channel_mask, - AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) { - ALOGV("createAudioPatch() profile not supported for device %08x", - devDesc->mDeviceType); + NULL, // updatedSamplingRate + patch->sources[0].format, + patch->sources[0].channel_mask, + AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) { + ALOGV("createAudioPatch() profile not supported for device %08x", devDesc->type()); return INVALID_OPERATION; } devices.add(devDesc); @@ -2674,7 +2089,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, if (patch->num_sinks > 1) { return INVALID_OPERATION; } - sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id); + sp<AudioInputDescriptor> inputDesc = mInputs.getInputFromId(patch->sinks[0].id); if (inputDesc == NULL) { return BAD_VALUE; } @@ -2689,7 +2104,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return BAD_VALUE; } - if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType, + if (!inputDesc->mProfile->isCompatibleProfile(devDesc->type(), devDesc->mAddress, patch->sinks[0].sample_rate, NULL, /*updatedSampleRate*/ @@ -2703,8 +2118,8 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, } // TODO: reconfigure output format and channels here ALOGV("createAudioPatch() setting device %08x on output %d", - devDesc->mDeviceType, inputDesc->mIoHandle); - setInputDevice(inputDesc->mIoHandle, devDesc->mDeviceType, true, handle); + devDesc->type(), inputDesc->mIoHandle); + setInputDevice(inputDesc->mIoHandle, devDesc->type(), true, handle); index = mAudioPatches.indexOfKey(*handle); if (index >= 0) { if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) { @@ -2754,8 +2169,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, return INVALID_OPERATION; } SortedVector<audio_io_handle_t> outputs = - getOutputsForDevice(sinkDeviceDesc->mDeviceType, - mOutputs); + getOutputsForDevice(sinkDeviceDesc->type(), mOutputs); // if the sink device is reachable via an opened output stream, request to go via // this output stream by adding a second source to the patch description audio_io_handle_t output = selectOutput(outputs, @@ -2785,8 +2199,7 @@ status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch, status, afPatchHandle); if (status == NO_ERROR) { if (index < 0) { - patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(), - &newPatch, uid); + patchDesc = new AudioPatch(&newPatch, uid); addAudioPatch(patchDesc->mHandle, patchDesc); } else { patchDesc->mPatch = newPatch; @@ -2829,7 +2242,7 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle, struct audio_patch *patch = &patchDesc->mPatch; patchDesc->mUid = mUidCached; if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) { - sp<AudioOutputDescriptor> outputDesc = getOutputFromId(patch->sources[0].id); + sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id); if (outputDesc == NULL) { ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id); return BAD_VALUE; @@ -2842,7 +2255,7 @@ status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle, NULL); } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) { if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { - sp<AudioInputDescriptor> inputDesc = getInputFromId(patch->sinks[0].id); + sp<AudioInputDescriptor> inputDesc = mInputs.getInputFromId(patch->sinks[0].id); if (inputDesc == NULL) { ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id); return BAD_VALUE; @@ -2872,30 +2285,11 @@ status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches, struct audio_patch *patches, unsigned int *generation) { - if (num_patches == NULL || (*num_patches != 0 && patches == NULL) || - generation == NULL) { + if (generation == NULL) { return BAD_VALUE; } - ALOGV("listAudioPatches() num_patches %d patches %p available patches %zu", - *num_patches, patches, mAudioPatches.size()); - if (patches == NULL) { - *num_patches = 0; - } - - size_t patchesWritten = 0; - size_t patchesMax = *num_patches; - for (size_t i = 0; - i < mAudioPatches.size() && patchesWritten < patchesMax; i++) { - patches[patchesWritten] = mAudioPatches[i]->mPatch; - patches[patchesWritten++].id = mAudioPatches[i]->mHandle; - ALOGV("listAudioPatches() patch %zu num_sources %d num_sinks %d", - i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks); - } - *num_patches = mAudioPatches.size(); - *generation = curAudioPortGeneration(); - ALOGV("listAudioPatches() got %zu patches needed %d", patchesWritten, *num_patches); - return NO_ERROR; + return mAudioPatches.listAudioPatches(num_patches, patches); } status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config) @@ -2914,7 +2308,7 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * sp<AudioPortConfig> audioPortConfig; if (config->type == AUDIO_PORT_TYPE_MIX) { if (config->role == AUDIO_PORT_ROLE_SOURCE) { - sp<AudioOutputDescriptor> outputDesc = getOutputFromId(config->id); + sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id); if (outputDesc == NULL) { return BAD_VALUE; } @@ -2923,7 +2317,7 @@ status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config * outputDesc->mIoHandle); audioPortConfig = outputDesc; } else if (config->role == AUDIO_PORT_ROLE_SINK) { - sp<AudioInputDescriptor> inputDesc = getInputFromId(config->id); + sp<AudioInputDescriptor> inputDesc = mInputs.getInputFromId(config->id); if (inputDesc == NULL) { return BAD_VALUE; } @@ -2980,63 +2374,12 @@ status_t AudioPolicyManager::acquireSoundTriggerSession(audio_session_t *session *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(); *device = getDeviceAndMixForInputSource(AUDIO_SOURCE_HOTWORD); - mSoundTriggerSessions.add(*session, *ioHandle); - - return NO_ERROR; -} - -status_t AudioPolicyManager::releaseSoundTriggerSession(audio_session_t session) -{ - ssize_t index = mSoundTriggerSessions.indexOfKey(session); - if (index < 0) { - ALOGW("acquireSoundTriggerSession() session %d not registered", session); - return BAD_VALUE; - } - - mSoundTriggerSessions.removeItem(session); - return NO_ERROR; -} - -status_t AudioPolicyManager::addAudioPatch(audio_patch_handle_t handle, - const sp<AudioPatch>& patch) -{ - ssize_t index = mAudioPatches.indexOfKey(handle); - - if (index >= 0) { - ALOGW("addAudioPatch() patch %d already in", handle); - return ALREADY_EXISTS; - } - mAudioPatches.add(handle, patch); - ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d" - "sink handle %d", - handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks, - patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id); - return NO_ERROR; -} - -status_t AudioPolicyManager::removeAudioPatch(audio_patch_handle_t handle) -{ - ssize_t index = mAudioPatches.indexOfKey(handle); - - if (index < 0) { - ALOGW("removeAudioPatch() patch %d not in", handle); - return ALREADY_EXISTS; - } - ALOGV("removeAudioPatch() handle %d af handle %d", handle, - mAudioPatches.valueAt(index)->mAfPatchHandle); - mAudioPatches.removeItemsAt(index); - return NO_ERROR; + return mSoundTriggerSessions.acquireSession(*session, *ioHandle); } // ---------------------------------------------------------------------------- // AudioPolicyManager // ---------------------------------------------------------------------------- - -uint32_t AudioPolicyManager::nextUniqueId() -{ - return android_atomic_inc(&mNextUniqueId); -} - uint32_t AudioPolicyManager::nextAudioPortGeneration() { return android_atomic_inc(&mAudioPortGeneration); @@ -3048,34 +2391,47 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa Thread(false), #endif //AUDIO_POLICY_TEST mPrimaryOutput((audio_io_handle_t)0), - mPhoneState(AUDIO_MODE_NORMAL), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), - mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), mA2dpSuspended(false), - mSpeakerDrcEnabled(false), mNextUniqueId(1), + mSpeakerDrcEnabled(false), mAudioPortGeneration(1), mBeaconMuteRefCount(0), mBeaconPlayingRefCount(0), mBeaconMuted(false) { + audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance(); + if (!engineInstance) { + ALOGE("%s: Could not get an instance of policy engine", __FUNCTION__); + return; + } + // Retrieve the Policy Manager Interface + mEngine = engineInstance->queryInterface<AudioPolicyManagerInterface>(); + if (mEngine == NULL) { + ALOGE("%s: Failed to get Policy Engine Interface", __FUNCTION__); + return; + } + mEngine->setObserver(this); + status_t status = mEngine->initCheck(); + ALOG_ASSERT(status == NO_ERROR, "Policy engine not initialized(err=%d)", status); + mUidCached = getuid(); mpClientInterface = clientInterface; - for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) { - mForceUse[i] = AUDIO_POLICY_FORCE_NONE; - } - - mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER); - if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) { - if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) { + mDefaultOutputDevice = new DeviceDescriptor(String8("Speaker"), AUDIO_DEVICE_OUT_SPEAKER); + if (ConfigParsingUtils::loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, + mHwModules, mAvailableInputDevices, mAvailableOutputDevices, + mDefaultOutputDevice, mSpeakerDrcEnabled) != NO_ERROR) { + if (ConfigParsingUtils::loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE, + mHwModules, mAvailableInputDevices, mAvailableOutputDevices, + mDefaultOutputDevice, mSpeakerDrcEnabled) != NO_ERROR) { ALOGE("could not load audio policy configuration file, setting defaults"); defaultAudioPolicyConfig(); } } // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices - // must be done after reading the policy - initializeVolumeCurves(); + // must be done after reading the policy (since conditionned by Speaker Drc Enabling) + mEngine->initializeVolumeCurves(mSpeakerDrcEnabled); // open all output streams needed to access attached devices audio_devices_t outputDeviceTypes = mAvailableOutputDevices.types(); @@ -3103,13 +2459,13 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa continue; } audio_devices_t profileType = outProfile->mSupportedDevices.types(); - if ((profileType & mDefaultOutputDevice->mDeviceType) != AUDIO_DEVICE_NONE) { - profileType = mDefaultOutputDevice->mDeviceType; + if ((profileType & mDefaultOutputDevice->type()) != AUDIO_DEVICE_NONE) { + profileType = mDefaultOutputDevice->type(); } else { // chose first device present in mSupportedDevices also part of // outputDeviceTypes for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) { - profileType = outProfile->mSupportedDevices[k]->mDeviceType; + profileType = outProfile->mSupportedDevices[k]->type(); if ((profileType & outputDeviceTypes) != 0) { break; } @@ -3144,13 +2500,12 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa outputDesc->mFormat = config.format; for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) { - audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType; + audio_devices_t type = outProfile->mSupportedDevices[k]->type(); ssize_t index = mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]); // give a valid ID to an attached device once confirmed it is reachable - if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) { - mAvailableOutputDevices[index]->mId = nextUniqueId(); - mAvailableOutputDevices[index]->mModule = mHwModules[i]; + if (index >= 0 && !mAvailableOutputDevices[index]->isAttached()) { + mAvailableOutputDevices[index]->attach(mHwModules[i]); } } if (mPrimaryOutput == 0 && @@ -3177,7 +2532,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa // inputDeviceTypes audio_devices_t profileType = AUDIO_DEVICE_NONE; for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) { - profileType = inProfile->mSupportedDevices[k]->mDeviceType; + profileType = inProfile->mSupportedDevices[k]->type(); if (profileType & inputDeviceTypes) { break; } @@ -3213,13 +2568,12 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa if (status == NO_ERROR) { for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) { - audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType; + audio_devices_t type = inProfile->mSupportedDevices[k]->type(); ssize_t index = mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]); // give a valid ID to an attached device once confirmed it is reachable - if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) { - mAvailableInputDevices[index]->mId = nextUniqueId(); - mAvailableInputDevices[index]->mModule = mHwModules[i]; + if (index >= 0 && !mAvailableInputDevices[index]->isAttached()) { + mAvailableInputDevices[index]->attach(mHwModules[i]); } } mpClientInterface->closeInput(input); @@ -3232,24 +2586,30 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa } // make sure all attached devices have been allocated a unique ID for (size_t i = 0; i < mAvailableOutputDevices.size();) { - if (mAvailableOutputDevices[i]->mId == 0) { - ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mDeviceType); + if (!mAvailableOutputDevices[i]->isAttached()) { + ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->type()); mAvailableOutputDevices.remove(mAvailableOutputDevices[i]); continue; } + // The device is now validated and can be appended to the available devices of the engine + mEngine->setDeviceConnectionState(mAvailableOutputDevices[i], + AUDIO_POLICY_DEVICE_STATE_AVAILABLE); i++; } for (size_t i = 0; i < mAvailableInputDevices.size();) { - if (mAvailableInputDevices[i]->mId == 0) { - ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mDeviceType); + if (!mAvailableInputDevices[i]->isAttached()) { + ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->type()); mAvailableInputDevices.remove(mAvailableInputDevices[i]); continue; } + // The device is now validated and can be appended to the available devices of the engine + mEngine->setDeviceConnectionState(mAvailableInputDevices[i], + AUDIO_POLICY_DEVICE_STATE_AVAILABLE); i++; } // make sure default device is reachable if (mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) { - ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mDeviceType); + ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->type()); } ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output"); @@ -3405,8 +2765,7 @@ bool AudioPolicyManager::threadLoop() audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle; - mOutputs.removeItem(mPrimaryOutput); - + removeOutput(mPrimaryOutput); sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL); outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER; audio_config_t config = AUDIO_CONFIG_INITIALIZER; @@ -3465,16 +2824,19 @@ int AudioPolicyManager::testOutputIndex(audio_io_handle_t output) void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc) { - outputDesc->mIoHandle = output; - outputDesc->mId = nextUniqueId(); + outputDesc->setIoHandle(output); mOutputs.add(output, outputDesc); nextAudioPortGeneration(); } +void AudioPolicyManager::removeOutput(audio_io_handle_t output) +{ + mOutputs.removeItem(output); +} + void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc) { - inputDesc->mIoHandle = input; - inputDesc->mId = nextUniqueId(); + inputDesc->setIoHandle(input); mInputs.add(input, inputDesc); nextAudioPortGeneration(); } @@ -3493,11 +2855,11 @@ void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc / } status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> devDesc, - audio_policy_dev_state_t state, - SortedVector<audio_io_handle_t>& outputs, - const String8 address) + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& outputs, + const String8 address) { - audio_devices_t device = devDesc->mDeviceType; + audio_devices_t device = devDesc->type(); sp<AudioOutputDescriptor> desc; // erase all current sample rates, formats and channel masks devDesc->clearCapabilities(); @@ -3507,7 +2869,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de for (size_t i = 0; i < mOutputs.size(); i++) { desc = mOutputs.valueAt(i); if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) { - if (!deviceDistinguishesOnAddress(device)) { + if (!device_distinguishes_on_address(device)) { ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i)); outputs.add(mOutputs.keyAt(i)); } else { @@ -3527,7 +2889,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de { sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j]; if (profile->mSupportedDevices.types() & device) { - if (!deviceDistinguishesOnAddress(device) || + if (!device_distinguishes_on_address(device) || address == profile->mSupportedDevices[0]->mAddress) { profiles.add(profile); ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i); @@ -3663,15 +3025,15 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de if (output != AUDIO_IO_HANDLE_NONE) { addOutput(output, desc); - if (deviceDistinguishesOnAddress(device) && address != "0") { - ssize_t index = mPolicyMixes.indexOfKey(address); - if (index >= 0) { - mPolicyMixes[index]->mOutput = desc; - desc->mPolicyMix = &mPolicyMixes[index]->mMix; - } else { + if (device_distinguishes_on_address(device) && address != "0") { + sp<AudioPolicyMix> policyMix; + if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) { ALOGE("checkOutputsForDevice() cannot find policy for address %s", address.string()); } + policyMix->setOutput(desc); + desc->mPolicyMix = &(policyMix->getMix()); + } else if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) { // no duplicated output for direct outputs and // outputs used by dynamic policy mixes @@ -3701,7 +3063,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de ALOGW("checkOutputsForDevice() could not open dup output for %d and %d", mPrimaryOutput, output); mpClientInterface->closeOutput(output); - mOutputs.removeItem(output); + removeOutput(output); nextAudioPortGeneration(); output = AUDIO_IO_HANDLE_NONE; } @@ -3718,7 +3080,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de outputs.add(output); devDesc->importAudioPort(profile); - if (deviceDistinguishesOnAddress(device)) { + if (device_distinguishes_on_address(device)) { ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)", device, address.string()); setOutputDevice(output, device, true/*force*/, 0/*delay*/, @@ -3738,7 +3100,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de desc = mOutputs.valueAt(i); if (!desc->isDuplicated()) { // exact match on device - if (deviceDistinguishesOnAddress(device) && + if (device_distinguishes_on_address(device) && (desc->mProfile->mSupportedDevices.types() == device)) { findIoHandlesByAddress(desc, device, address, outputs); } else if (!(desc->mProfile->mSupportedDevices.types() @@ -3781,9 +3143,9 @@ status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> de } status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, - audio_policy_dev_state_t state, - SortedVector<audio_io_handle_t>& inputs, - const String8 address) + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& inputs, + const String8 address) { sp<AudioInputDescriptor> desc; if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { @@ -3810,7 +3172,7 @@ status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, sp<IOProfile> profile = mHwModules[module_idx]->mInputProfiles[profile_index]; if (profile->mSupportedDevices.types() & (device & ~AUDIO_DEVICE_BIT_IN)) { - if (!deviceDistinguishesOnAddress(device) || + if (!device_distinguishes_on_address(device) || address == profile->mSupportedDevices[0]->mAddress) { profiles.add(profile); ALOGV("checkInputsForDevice(): adding profile %zu from module %zu", @@ -3982,12 +3344,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) ALOGW("closeOutput() unknown output %d", output); return; } - - for (size_t i = 0; i < mPolicyMixes.size(); i++) { - if (mPolicyMixes[i]->mOutput == outputDesc) { - mPolicyMixes[i]->mOutput.clear(); - } - } + mPolicyMixes.closeOutput(outputDesc); // look for duplicated outputs connected to the output being removed. for (size_t i = 0; i < mOutputs.size(); i++) { @@ -4013,7 +3370,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); mpClientInterface->closeOutput(duplicatedOutput); - mOutputs.removeItem(duplicatedOutput); + removeOutput(duplicatedOutput); } } @@ -4032,7 +3389,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output) mpClientInterface->setParameters(output, param.toString()); mpClientInterface->closeOutput(output); - mOutputs.removeItem(output); + removeOutput(output); mPreviousOutputs = mOutputs; } @@ -4061,7 +3418,7 @@ void AudioPolicyManager::closeInput(audio_io_handle_t input) } SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device, - DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs) + AudioOutputCollection openOutputs) { SortedVector<audio_io_handle_t> outputs; @@ -4078,7 +3435,7 @@ SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_de } bool AudioPolicyManager::vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, - SortedVector<audio_io_handle_t>& outputs2) + SortedVector<audio_io_handle_t>& outputs2) { if (outputs1.size() != outputs2.size()) { return false; @@ -4122,7 +3479,7 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) // mute strategy while moving tracks from one output to another for (size_t i = 0; i < srcOutputs.size(); i++) { sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]); - if (desc->isStrategyActive(strategy)) { + if (isStrategyActive(desc, strategy)) { setStrategyMute(strategy, true, srcOutputs[i]); setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice); } @@ -4161,10 +3518,10 @@ void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) void AudioPolicyManager::checkOutputForAllStrategies() { - if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) + if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); checkOutputForStrategy(STRATEGY_PHONE); - if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) + if (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); checkOutputForStrategy(STRATEGY_SONIFICATION); checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); @@ -4174,21 +3531,9 @@ void AudioPolicyManager::checkOutputForAllStrategies() checkOutputForStrategy(STRATEGY_REROUTING); } -audio_io_handle_t AudioPolicyManager::getA2dpOutput() -{ - for (size_t i = 0; i < mOutputs.size(); i++) { - sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); - if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { - return mOutputs.keyAt(i); - } - } - - return 0; -} - void AudioPolicyManager::checkA2dpSuspend() { - audio_io_handle_t a2dpOutput = getA2dpOutput(); + audio_io_handle_t a2dpOutput = mOutputs.getA2dpOutput(); if (a2dpOutput == 0) { mA2dpSuspended = false; return; @@ -4212,20 +3557,20 @@ void AudioPolicyManager::checkA2dpSuspend() // if (mA2dpSuspended) { if ((!isScoConnected || - ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] != AUDIO_POLICY_FORCE_BT_SCO))) && - ((mPhoneState != AUDIO_MODE_IN_CALL) && - (mPhoneState != AUDIO_MODE_RINGTONE))) { + ((mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION) != AUDIO_POLICY_FORCE_BT_SCO) && + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) != AUDIO_POLICY_FORCE_BT_SCO))) && + ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) && + (mEngine->getPhoneState() != AUDIO_MODE_RINGTONE))) { mpClientInterface->restoreOutput(a2dpOutput); mA2dpSuspended = false; } } else { if ((isScoConnected && - ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || - (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO))) || - ((mPhoneState == AUDIO_MODE_IN_CALL) || - (mPhoneState == AUDIO_MODE_RINGTONE))) { + ((mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION) == AUDIO_POLICY_FORCE_BT_SCO) || + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_RECORD) == AUDIO_POLICY_FORCE_BT_SCO))) || + ((mEngine->getPhoneState() == AUDIO_MODE_IN_CALL) || + (mEngine->getPhoneState() == AUDIO_MODE_RINGTONE))) { mpClientInterface->suspendOutput(a2dpOutput); mA2dpSuspended = true; @@ -4268,27 +3613,27 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, // use device for strategy DTMF // 9: the strategy for beacon, a.k.a. "transmitted through speaker" is active on the output: // use device for strategy t-t-s - if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE) && - mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE) && + mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); } else if (isInCall() || - outputDesc->isStrategyActive(STRATEGY_PHONE)) { + isStrategyActive(outputDesc, STRATEGY_PHONE)) { device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) { + } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) { device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION)) { + } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION)) { device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) { + } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) { device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_ACCESSIBILITY)) { + } else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) { device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_MEDIA)) { + } else if (isStrategyActive(outputDesc, STRATEGY_MEDIA)) { device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { + } else if (isStrategyActive(outputDesc, STRATEGY_DTMF)) { device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { + } else if (isStrategyActive(outputDesc, STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { device = getDeviceForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); - } else if (outputDesc->isStrategyActive(STRATEGY_REROUTING)) { + } else if (isStrategyActive(outputDesc, STRATEGY_REROUTING)) { device = getDeviceForStrategy(STRATEGY_REROUTING, fromCache); } @@ -4328,12 +3673,12 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre return AUDIO_DEVICE_NONE; } audio_devices_t devices; - AudioPolicyManager::routing_strategy strategy = getStrategy(stream); + routing_strategy strategy = getStrategy(stream); devices = getDeviceForStrategy(strategy, true /*fromCache*/); SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs); for (size_t i = 0; i < outputs.size(); i++) { sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); - if (outputDesc->isStrategyActive(strategy)) { + if (isStrategyActive(outputDesc, strategy)) { devices = outputDesc->device(); break; } @@ -4349,39 +3694,10 @@ audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stre return devices; } -AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( - audio_stream_type_t stream) { - +routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) const +{ ALOG_ASSERT(stream != AUDIO_STREAM_PATCH,"getStrategy() called for AUDIO_STREAM_PATCH"); - - // stream to strategy mapping - switch (stream) { - case AUDIO_STREAM_VOICE_CALL: - case AUDIO_STREAM_BLUETOOTH_SCO: - return STRATEGY_PHONE; - case AUDIO_STREAM_RING: - case AUDIO_STREAM_ALARM: - return STRATEGY_SONIFICATION; - case AUDIO_STREAM_NOTIFICATION: - return STRATEGY_SONIFICATION_RESPECTFUL; - case AUDIO_STREAM_DTMF: - return STRATEGY_DTMF; - default: - ALOGE("unknown stream type %d", stream); - case AUDIO_STREAM_SYSTEM: - // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs - // while key clicks are played produces a poor result - case AUDIO_STREAM_MUSIC: - return STRATEGY_MEDIA; - case AUDIO_STREAM_ENFORCED_AUDIBLE: - return STRATEGY_ENFORCED_AUDIBLE; - case AUDIO_STREAM_TTS: - return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; - case AUDIO_STREAM_ACCESSIBILITY: - return STRATEGY_ACCESSIBILITY; - case AUDIO_STREAM_REROUTING: - return STRATEGY_REROUTING; - } + return mEngine->getStrategyForStream(stream); } uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { @@ -4392,45 +3708,8 @@ uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { return (uint32_t) STRATEGY_ENFORCED_AUDIBLE; } - // usage to strategy mapping - switch (attr->usage) { - case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - if (isStreamActive(AUDIO_STREAM_RING) || isStreamActive(AUDIO_STREAM_ALARM)) { - return (uint32_t) STRATEGY_SONIFICATION; - } - if (isInCall()) { - return (uint32_t) STRATEGY_PHONE; - } - return (uint32_t) STRATEGY_ACCESSIBILITY; - - case AUDIO_USAGE_MEDIA: - case AUDIO_USAGE_GAME: - case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return (uint32_t) STRATEGY_MEDIA; - - case AUDIO_USAGE_VOICE_COMMUNICATION: - return (uint32_t) STRATEGY_PHONE; - - case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return (uint32_t) STRATEGY_DTMF; - - case AUDIO_USAGE_ALARM: - case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return (uint32_t) STRATEGY_SONIFICATION; - - case AUDIO_USAGE_NOTIFICATION: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - case AUDIO_USAGE_NOTIFICATION_EVENT: - return (uint32_t) STRATEGY_SONIFICATION_RESPECTFUL; - - case AUDIO_USAGE_UNKNOWN: - default: - return (uint32_t) STRATEGY_MEDIA; - } + return static_cast<uint32_t>(mEngine->getStrategyForUsage(attr->usage)); } void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) { @@ -4444,21 +3723,6 @@ void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t } } -bool AudioPolicyManager::isAnyOutputActive(audio_stream_type_t streamToIgnore) { - for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) { - if (s == (size_t) streamToIgnore) { - continue; - } - for (size_t i = 0; i < mOutputs.size(); i++) { - const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); - if (outputDesc->mRefCount[s] != 0) { - return true; - } - } - } - return false; -} - uint32_t AudioPolicyManager::handleEventForBeacon(int event) { switch(event) { case STARTING_OUTPUT: @@ -4513,292 +3777,14 @@ uint32_t AudioPolicyManager::setBeaconMute(bool mute) { } audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, - bool fromCache) + bool fromCache) { - uint32_t device = AUDIO_DEVICE_NONE; - if (fromCache) { ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]); return mDeviceForStrategy[strategy]; } - audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); - switch (strategy) { - - case STRATEGY_TRANSMITTED_THROUGH_SPEAKER: - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; - if (!device) { - ALOGE("getDeviceForStrategy() no device found for "\ - "STRATEGY_TRANSMITTED_THROUGH_SPEAKER"); - } - break; - - case STRATEGY_SONIFICATION_RESPECTFUL: - if (isInCall()) { - device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); - } else if (isStreamActiveRemotely(AUDIO_STREAM_MUSIC, - SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { - // while media is playing on a remote device, use the the sonification behavior. - // Note that we test this usecase before testing if media is playing because - // the isStreamActive() method only informs about the activity of a stream, not - // if it's for local playback. Note also that we use the same delay between both tests - device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); - //user "safe" speaker if available instead of normal speaker to avoid triggering - //other acoustic safety mechanisms for notification - if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) - device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; - } else if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { - // while media is playing (or has recently played), use the same device - device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); - } else { - // when media is not playing anymore, fall back on the sonification behavior - device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); - //user "safe" speaker if available instead of normal speaker to avoid triggering - //other acoustic safety mechanisms for notification - if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) - device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; - } - - break; - - case STRATEGY_DTMF: - if (!isInCall()) { - // when off call, DTMF strategy follows the same rules as MEDIA strategy - device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); - break; - } - // when in call, DTMF and PHONE strategies follow the same rules - // FALL THROUGH - - case STRATEGY_PHONE: - // Force use of only devices on primary output if: - // - in call AND - // - cannot route from voice call RX OR - // - audio HAL version is < 3.0 and TX device is on the primary HW module - if (mPhoneState == AUDIO_MODE_IN_CALL) { - audio_devices_t txDevice = - getDeviceAndMixForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); - sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); - if (((mAvailableInputDevices.types() & - AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || - (((txDevice & availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN) != 0) && - (hwOutputDesc->getAudioPort()->mModule->mHalVersion < - AUDIO_DEVICE_API_VERSION_3_0))) { - availableOutputDeviceTypes = availablePrimaryOutputDevices(); - } - } - // for phone strategy, we first consider the forced use and then the available devices by order - // of priority - switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { - case AUDIO_POLICY_FORCE_BT_SCO: - if (!isInCall() || strategy != STRATEGY_DTMF) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; - if (device) break; - } - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; - if (device) break; - // if SCO device is requested but no SCO device is available, fall back to default case - // FALL THROUGH - - default: // FORCE_NONE - // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP - if (!isInCall() && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (getA2dpOutput() != 0)) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; - if (device) break; - } - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; - if (device) break; - if (mPhoneState != AUDIO_MODE_IN_CALL) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; - } - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE; - if (device) break; - device = mDefaultOutputDevice->mDeviceType; - if (device == AUDIO_DEVICE_NONE) { - ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); - } - break; - - case AUDIO_POLICY_FORCE_SPEAKER: - // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to - // A2DP speaker when forcing to speaker output - if (!isInCall() && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (getA2dpOutput() != 0)) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; - if (device) break; - } - if (!isInCall()) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; - if (device) break; - } - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; - if (device) break; - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; - if (device) break; - device = mDefaultOutputDevice->mDeviceType; - if (device == AUDIO_DEVICE_NONE) { - ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); - } - break; - } - break; - - case STRATEGY_SONIFICATION: - - // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by - // handleIncallSonification(). - if (isInCall()) { - device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); - break; - } - // FALL THROUGH - - case STRATEGY_ENFORCED_AUDIBLE: - // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION - // except: - // - when in call where it doesn't default to STRATEGY_PHONE behavior - // - in countries where not enforced in which case it follows STRATEGY_MEDIA - - if ((strategy == STRATEGY_SONIFICATION) || - (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { - device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; - if (device == AUDIO_DEVICE_NONE) { - ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); - } - } - // The second device used for sonification is the same as the device used by media strategy - // FALL THROUGH - - // FIXME: STRATEGY_ACCESSIBILITY and STRATEGY_REROUTING follow STRATEGY_MEDIA for now - case STRATEGY_ACCESSIBILITY: - if (strategy == STRATEGY_ACCESSIBILITY) { - // do not route accessibility prompts to a digital output currently configured with a - // compressed format as they would likely not be mixed and dropped. - for (size_t i = 0; i < mOutputs.size(); i++) { - sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); - audio_devices_t devices = desc->device() & - (AUDIO_DEVICE_OUT_HDMI | AUDIO_DEVICE_OUT_SPDIF | AUDIO_DEVICE_OUT_HDMI_ARC); - if (desc->isActive() && !audio_is_linear_pcm(desc->mFormat) && - devices != AUDIO_DEVICE_NONE) { - availableOutputDeviceTypes = availableOutputDeviceTypes & ~devices; - } - } - } - // FALL THROUGH - - case STRATEGY_REROUTING: - case STRATEGY_MEDIA: { - uint32_t device2 = AUDIO_DEVICE_NONE; - if (strategy != STRATEGY_SONIFICATION) { - // no sonification on remote submix (e.g. WFD) - if (mAvailableOutputDevices.getDevice(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0")) != 0) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; - } - } - if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && - (getA2dpOutput() != 0)) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; - } - } - if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] == AUDIO_POLICY_FORCE_SPEAKER)) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; - } - if ((device2 == AUDIO_DEVICE_NONE)) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; - } - if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { - // no sonification on aux digital (e.g. HDMI) - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; - } - if ((device2 == AUDIO_DEVICE_NONE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; - } - if (device2 == AUDIO_DEVICE_NONE) { - device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; - } - int device3 = AUDIO_DEVICE_NONE; - if (strategy == STRATEGY_MEDIA) { - // ARC, SPDIF and AUX_LINE can co-exist with others. - device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_HDMI_ARC; - device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPDIF); - device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_LINE); - } - - device2 |= device3; - // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or - // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise - device |= device2; - - // If hdmi system audio mode is on, remove speaker out of output list. - if ((strategy == STRATEGY_MEDIA) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == - AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { - device &= ~AUDIO_DEVICE_OUT_SPEAKER; - } - - if (device) break; - device = mDefaultOutputDevice->mDeviceType; - if (device == AUDIO_DEVICE_NONE) { - ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); - } - } break; - - default: - ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); - break; - } - - ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); - return device; + return mEngine->getDeviceForStrategy(strategy); } void AudioPolicyManager::updateDevicesAndOutputs() @@ -4849,7 +3835,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d", mute ? "muting" : "unmuting", i, curDevice, curOutput); setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs); - if (desc->isStrategyActive((routing_strategy)i)) { + if (isStrategyActive(desc, (routing_strategy)i)) { if (mute) { // FIXME: should not need to double latency if volume could be applied // immediately by the audioflinger mixer. We must account for the delay @@ -4872,7 +3858,7 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> muteWaitMs = outputDesc->latency() * 2; } for (size_t i = 0; i < NUM_STRATEGIES; i++) { - if (outputDesc->isStrategyActive((routing_strategy)i)) { + if (isStrategyActive(outputDesc, (routing_strategy)i)) { setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle); // do tempMute unmute after twice the mute wait time setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle, @@ -4978,8 +3964,7 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output, status, afPatchHandle, patch.num_sources, patch.num_sinks); if (status == NO_ERROR) { if (index < 0) { - patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(), - &patch, mUidCached); + patchDesc = new AudioPatch(&patch, mUidCached); addAudioPatch(patchDesc->mHandle, patchDesc); } else { patchDesc->mPatch = patch; @@ -4998,7 +3983,7 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output, // inform all input as well for (size_t i = 0; i < mInputs.size(); i++) { const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i); - if (!isVirtualInputDevice(inputDescriptor->mDevice)) { + if (!is_virtual_input_device(inputDescriptor->mDevice)) { AudioParameter inputCmd = AudioParameter(); ALOGV("%s: inform input %d of device:%d", __func__, inputDescriptor->mIoHandle, device); @@ -5085,8 +4070,7 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input, status, afPatchHandle); if (status == NO_ERROR) { if (index < 0) { - patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(), - &patch, mUidCached); + patchDesc = new AudioPatch(&patch, mUidCached); addAudioPatch(patchDesc->mHandle, patchDesc); } else { patchDesc->mPatch = patch; @@ -5128,12 +4112,12 @@ status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input, return status; } -sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device, - String8 address, - uint32_t& samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_input_flags_t flags) +sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device, + String8 address, + uint32_t& samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) { // Choose an input profile based on the requested capture parameters: select the first available // profile supporting all requested parameters. @@ -5160,500 +4144,35 @@ sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devi audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource, - AudioMix **policyMix) + AudioMix **policyMix) { - audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & - ~AUDIO_DEVICE_BIT_IN; + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; + audio_devices_t selectedDeviceFromMix = + mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix); - for (size_t i = 0; i < mPolicyMixes.size(); i++) { - if (mPolicyMixes[i]->mMix.mMixType != MIX_TYPE_RECORDERS) { - continue; - } - for (size_t j = 0; j < mPolicyMixes[i]->mMix.mCriteria.size(); j++) { - if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && - mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource == inputSource) || - (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mPolicyMixes[i]->mMix.mCriteria[j].mRule && - mPolicyMixes[i]->mMix.mCriteria[j].mAttr.mSource != inputSource)) { - if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { - if (policyMix != NULL) { - *policyMix = &mPolicyMixes[i]->mMix; - } - return AUDIO_DEVICE_IN_REMOTE_SUBMIX; - } - break; - } - } + if (selectedDeviceFromMix != AUDIO_DEVICE_NONE) { + return selectedDeviceFromMix; } - return getDeviceForInputSource(inputSource); } audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) { - uint32_t device = AUDIO_DEVICE_NONE; - audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & - ~AUDIO_DEVICE_BIT_IN; - - switch (inputSource) { - case AUDIO_SOURCE_VOICE_UPLINK: - if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { - device = AUDIO_DEVICE_IN_VOICE_CALL; - break; - } - break; - - case AUDIO_SOURCE_DEFAULT: - case AUDIO_SOURCE_MIC: - if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { - device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; - } else if ((mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO) && - (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) { - device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { - device = AUDIO_DEVICE_IN_WIRED_HEADSET; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { - device = AUDIO_DEVICE_IN_USB_DEVICE; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { - device = AUDIO_DEVICE_IN_BUILTIN_MIC; - } - break; - - case AUDIO_SOURCE_VOICE_COMMUNICATION: - // Allow only use of devices on primary input if in call and HAL does not support routing - // to voice call path. - if ((mPhoneState == AUDIO_MODE_IN_CALL) && - (mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { - availableDeviceTypes = availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN; - } - - switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { - case AUDIO_POLICY_FORCE_BT_SCO: - // if SCO device is requested but no SCO device is available, fall back to default case - if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { - device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; - break; - } - // FALL THROUGH - - default: // FORCE_NONE - if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { - device = AUDIO_DEVICE_IN_WIRED_HEADSET; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { - device = AUDIO_DEVICE_IN_USB_DEVICE; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { - device = AUDIO_DEVICE_IN_BUILTIN_MIC; - } - break; - - case AUDIO_POLICY_FORCE_SPEAKER: - if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { - device = AUDIO_DEVICE_IN_BACK_MIC; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { - device = AUDIO_DEVICE_IN_BUILTIN_MIC; - } - break; - } - break; - - case AUDIO_SOURCE_VOICE_RECOGNITION: - case AUDIO_SOURCE_HOTWORD: - if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && - availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { - device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { - device = AUDIO_DEVICE_IN_WIRED_HEADSET; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { - device = AUDIO_DEVICE_IN_USB_DEVICE; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { - device = AUDIO_DEVICE_IN_BUILTIN_MIC; - } - break; - case AUDIO_SOURCE_CAMCORDER: - if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { - device = AUDIO_DEVICE_IN_BACK_MIC; - } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { - device = AUDIO_DEVICE_IN_BUILTIN_MIC; - } - break; - case AUDIO_SOURCE_VOICE_DOWNLINK: - case AUDIO_SOURCE_VOICE_CALL: - if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { - device = AUDIO_DEVICE_IN_VOICE_CALL; - } - break; - case AUDIO_SOURCE_REMOTE_SUBMIX: - if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { - device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; - } - break; - case AUDIO_SOURCE_FM_TUNER: - if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) { - device = AUDIO_DEVICE_IN_FM_TUNER; - } - break; - default: - ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); - break; - } - ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); - return device; -} - -bool AudioPolicyManager::isVirtualInputDevice(audio_devices_t device) -{ - if ((device & AUDIO_DEVICE_BIT_IN) != 0) { - device &= ~AUDIO_DEVICE_BIT_IN; - if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) - return true; - } - return false; -} - -bool AudioPolicyManager::deviceDistinguishesOnAddress(audio_devices_t device) { - return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL & ~AUDIO_DEVICE_BIT_IN) != 0); -} - -audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs) -{ - for (size_t i = 0; i < mInputs.size(); i++) { - const sp<AudioInputDescriptor> input_descriptor = mInputs.valueAt(i); - if ((input_descriptor->mRefCount > 0) - && (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) { - return mInputs.keyAt(i); - } - } - return 0; -} - -uint32_t AudioPolicyManager::activeInputsCount() const -{ - uint32_t count = 0; - for (size_t i = 0; i < mInputs.size(); i++) { - const sp<AudioInputDescriptor> desc = mInputs.valueAt(i); - if (desc->mRefCount > 0) { - count++; - } - } - return count; -} - - -audio_devices_t AudioPolicyManager::getDeviceForVolume(audio_devices_t device) -{ - if (device == AUDIO_DEVICE_NONE) { - // this happens when forcing a route update and no track is active on an output. - // In this case the returned category is not important. - device = AUDIO_DEVICE_OUT_SPEAKER; - } else if (popcount(device) > 1) { - // Multiple device selection is either: - // - speaker + one other device: give priority to speaker in this case. - // - one A2DP device + another device: happens with duplicated output. In this case - // retain the device on the A2DP output as the other must not correspond to an active - // selection if not the speaker. - // - HDMI-CEC system audio mode only output: give priority to available item in order. - if (device & AUDIO_DEVICE_OUT_SPEAKER) { - device = AUDIO_DEVICE_OUT_SPEAKER; - } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { - device = AUDIO_DEVICE_OUT_HDMI_ARC; - } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { - device = AUDIO_DEVICE_OUT_AUX_LINE; - } else if (device & AUDIO_DEVICE_OUT_SPDIF) { - device = AUDIO_DEVICE_OUT_SPDIF; - } else { - device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); - } - } - - /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ - if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) - device = AUDIO_DEVICE_OUT_SPEAKER; - - ALOGW_IF(popcount(device) != 1, - "getDeviceForVolume() invalid device combination: %08x", - device); - - return device; -} - -AudioPolicyManager::device_category AudioPolicyManager::getDeviceCategory(audio_devices_t device) -{ - switch(getDeviceForVolume(device)) { - case AUDIO_DEVICE_OUT_EARPIECE: - return DEVICE_CATEGORY_EARPIECE; - case AUDIO_DEVICE_OUT_WIRED_HEADSET: - case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: - case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: - case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: - case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: - case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: - return DEVICE_CATEGORY_HEADSET; - case AUDIO_DEVICE_OUT_LINE: - case AUDIO_DEVICE_OUT_AUX_DIGITAL: - /*USB? Remote submix?*/ - return DEVICE_CATEGORY_EXT_MEDIA; - case AUDIO_DEVICE_OUT_SPEAKER: - case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: - case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: - case AUDIO_DEVICE_OUT_USB_ACCESSORY: - case AUDIO_DEVICE_OUT_USB_DEVICE: - case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: - default: - return DEVICE_CATEGORY_SPEAKER; - } -} - -/* static */ -float AudioPolicyManager::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, - int indexInUi) -{ - device_category deviceCategory = getDeviceCategory(device); - const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; - - // the volume index in the UI is relative to the min and max volume indices for this stream type - int nbSteps = 1 + curve[VOLMAX].mIndex - - curve[VOLMIN].mIndex; - int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / - (streamDesc.mIndexMax - streamDesc.mIndexMin); - - // find what part of the curve this index volume belongs to, or if it's out of bounds - int segment = 0; - if (volIdx < curve[VOLMIN].mIndex) { // out of bounds - return 0.0f; - } else if (volIdx < curve[VOLKNEE1].mIndex) { - segment = 0; - } else if (volIdx < curve[VOLKNEE2].mIndex) { - segment = 1; - } else if (volIdx <= curve[VOLMAX].mIndex) { - segment = 2; - } else { // out of bounds - return 1.0f; - } - - // linear interpolation in the attenuation table in dB - float decibels = curve[segment].mDBAttenuation + - ((float)(volIdx - curve[segment].mIndex)) * - ( (curve[segment+1].mDBAttenuation - - curve[segment].mDBAttenuation) / - ((float)(curve[segment+1].mIndex - - curve[segment].mIndex)) ); - - float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) - - ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", - curve[segment].mIndex, volIdx, - curve[segment+1].mIndex, - curve[segment].mDBAttenuation, - decibels, - curve[segment+1].mDBAttenuation, - amplification); - - return amplification; -} - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sDefaultVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { - {1, -55.0f}, {20, -43.0f}, {86, -12.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { - {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} -}; - -// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks -// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. -// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). -// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { - {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { - {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sLinearVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sSilentVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - AudioPolicyManager::sFullScaleVolumeCurve[AudioPolicyManager::VOLCNT] = { - {0, 0.0f}, {1, 0.0f}, {2, 0.0f}, {100, 0.0f} -}; - -const AudioPolicyManager::VolumeCurvePoint - *AudioPolicyManager::sVolumeProfiles[AUDIO_STREAM_CNT] - [AudioPolicyManager::DEVICE_CATEGORY_CNT] = { - { // AUDIO_STREAM_VOICE_CALL - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_SYSTEM - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_RING - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_MUSIC - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_ALARM - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_NOTIFICATION - sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_BLUETOOTH_SCO - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_ENFORCED_AUDIBLE - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_DTMF - sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_TTS - // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER - sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET - sLinearVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sSilentVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_ACCESSIBILITY - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_REROUTING - sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET - sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, - { // AUDIO_STREAM_PATCH - sFullScaleVolumeCurve, // DEVICE_CATEGORY_HEADSET - sFullScaleVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sFullScaleVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sFullScaleVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA - }, -}; - -void AudioPolicyManager::initializeVolumeCurves() -{ - for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { - mStreams[i].mVolumeCurve[j] = - sVolumeProfiles[i][j]; - } - } - - // Check availability of DRC on speaker path: if available, override some of the speaker curves - if (mSpeakerDrcEnabled) { - mStreams[AUDIO_STREAM_SYSTEM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sDefaultSystemVolumeCurveDrc; - mStreams[AUDIO_STREAM_RING].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerSonificationVolumeCurveDrc; - mStreams[AUDIO_STREAM_ALARM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerSonificationVolumeCurveDrc; - mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerSonificationVolumeCurveDrc; - mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerMediaVolumeCurveDrc; - mStreams[AUDIO_STREAM_ACCESSIBILITY].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = - sSpeakerMediaVolumeCurveDrc; - } + return mEngine->getDeviceForInputSource(inputSource); } float AudioPolicyManager::computeVolume(audio_stream_type_t stream, - int index, - audio_io_handle_t output, - audio_devices_t device) + int index, + audio_io_handle_t output, + audio_devices_t device) { float volume = 1.0; sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); - StreamDescriptor &streamDesc = mStreams[stream]; if (device == AUDIO_DEVICE_NONE) { device = outputDesc->device(); } - - volume = volIndexToAmpl(device, streamDesc, index); + volume = mEngine->volIndexToAmpl(Volume::getDeviceCategory(device), stream, index); // if a headset is connected, apply the following rules to ring tones and notifications // to avoid sound level bursts in user's ears: @@ -5669,8 +4188,8 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) || (stream == AUDIO_STREAM_SYSTEM) || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && - (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) && - streamDesc.mCanBeMuted) { + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && + mStreams.canBeMuted(stream)) { volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was @@ -5695,11 +4214,11 @@ float AudioPolicyManager::computeVolume(audio_stream_type_t stream, } status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, - int index, - audio_io_handle_t output, - audio_devices_t device, - int delayMs, - bool force) + int index, + audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) { // do not change actual stream volume if the stream is muted @@ -5708,14 +4227,13 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, stream, mOutputs.valueFor(output)->mMuteCount[stream]); return NO_ERROR; } - + audio_policy_forced_cfg_t forceUseForComm = + mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION); // do not change in call volume if bluetooth is connected and vice versa - if ((stream == AUDIO_STREAM_VOICE_CALL && - mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || - (stream == AUDIO_STREAM_BLUETOOTH_SCO && - mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO)) { + if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) || + (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) { ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", - stream, mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + stream, forceUseForComm); return INVALID_OPERATION; } @@ -5752,7 +4270,7 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, float voiceVolume; // Force voice volume to max for bluetooth SCO as volume is managed by the headset if (stream == AUDIO_STREAM_VOICE_CALL) { - voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + voiceVolume = (float)index/(float)mStreams[stream].getVolumeIndexMax(); } else { voiceVolume = 1.0; } @@ -5767,9 +4285,9 @@ status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, } void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output, - audio_devices_t device, - int delayMs, - bool force) + audio_devices_t device, + int delayMs, + bool force) { ALOGVV("applyStreamVolumes() for output %d and device %x", output, device); @@ -5787,10 +4305,10 @@ void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output, } void AudioPolicyManager::setStrategyMute(routing_strategy strategy, - bool on, - audio_io_handle_t output, - int delayMs, - audio_devices_t device) + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) { ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { @@ -5804,12 +4322,12 @@ void AudioPolicyManager::setStrategyMute(routing_strategy strategy, } void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, - bool on, - audio_io_handle_t output, - int delayMs, - audio_devices_t device) + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) { - StreamDescriptor &streamDesc = mStreams[stream]; + const StreamDescriptor &streamDesc = mStreams[stream]; sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); if (device == AUDIO_DEVICE_NONE) { device = outputDesc->device(); @@ -5820,9 +4338,9 @@ void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, if (on) { if (outputDesc->mMuteCount[stream] == 0) { - if (streamDesc.mCanBeMuted && + if (streamDesc.canBeMuted() && ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || - (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) { + (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) { checkAndSetVolume(stream, 0, output, device, delayMs); } } @@ -5888,2103 +4406,12 @@ void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, } } -bool AudioPolicyManager::isInCall() -{ - return isStateInCall(mPhoneState); -} - -bool AudioPolicyManager::isStateInCall(int state) { - return ((state == AUDIO_MODE_IN_CALL) || - (state == AUDIO_MODE_IN_COMMUNICATION)); -} - -uint32_t AudioPolicyManager::getMaxEffectsCpuLoad() -{ - return MAX_EFFECTS_CPU_LOAD; -} - -uint32_t AudioPolicyManager::getMaxEffectsMemory() -{ - return MAX_EFFECTS_MEMORY; -} - - -// --- AudioOutputDescriptor class implementation - -AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( - const sp<IOProfile>& profile) - : mId(0), mIoHandle(0), mLatency(0), - mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), - mPatchHandle(0), - mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0) -{ - // clear usage count for all stream types - for (int i = 0; i < AUDIO_STREAM_CNT; i++) { - mRefCount[i] = 0; - mCurVolume[i] = -1.0; - mMuteCount[i] = 0; - mStopTime[i] = 0; - } - for (int i = 0; i < NUM_STRATEGIES; i++) { - mStrategyMutedByDevice[i] = false; - } - if (profile != NULL) { - mFlags = (audio_output_flags_t)profile->mFlags; - mSamplingRate = profile->pickSamplingRate(); - mFormat = profile->pickFormat(); - mChannelMask = profile->pickChannelMask(); - if (profile->mGains.size() > 0) { - profile->mGains[0]->getDefaultConfig(&mGain); - } - } -} - -audio_devices_t AudioPolicyManager::AudioOutputDescriptor::device() const -{ - if (isDuplicated()) { - return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); - } else { - return mDevice; - } -} - -uint32_t AudioPolicyManager::AudioOutputDescriptor::latency() -{ - if (isDuplicated()) { - return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; - } else { - return mLatency; - } -} - -bool AudioPolicyManager::AudioOutputDescriptor::sharesHwModuleWith( - const sp<AudioOutputDescriptor> outputDesc) -{ - if (isDuplicated()) { - return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); - } else if (outputDesc->isDuplicated()){ - return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2); - } else { - return (mProfile->mModule == outputDesc->mProfile->mModule); - } -} - -void AudioPolicyManager::AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, - int delta) -{ - // forward usage count change to attached outputs - if (isDuplicated()) { - mOutput1->changeRefCount(stream, delta); - mOutput2->changeRefCount(stream, delta); - } - if ((delta + (int)mRefCount[stream]) < 0) { - ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", - delta, stream, mRefCount[stream]); - mRefCount[stream] = 0; - return; - } - mRefCount[stream] += delta; - ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); -} - -audio_devices_t AudioPolicyManager::AudioOutputDescriptor::supportedDevices() -{ - if (isDuplicated()) { - return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); - } else { - return mProfile->mSupportedDevices.types() ; - } -} - -bool AudioPolicyManager::AudioOutputDescriptor::isActive(uint32_t inPastMs) const -{ - return isStrategyActive(NUM_STRATEGIES, inPastMs); -} - -bool AudioPolicyManager::AudioOutputDescriptor::isStrategyActive(routing_strategy strategy, - uint32_t inPastMs, - nsecs_t sysTime) const -{ - if ((sysTime == 0) && (inPastMs != 0)) { - sysTime = systemTime(); - } - for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { - if (i == AUDIO_STREAM_PATCH) { - continue; - } - if (((getStrategy((audio_stream_type_t)i) == strategy) || - (NUM_STRATEGIES == strategy)) && - isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { - return true; - } - } - return false; -} - -bool AudioPolicyManager::AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, - uint32_t inPastMs, - nsecs_t sysTime) const -{ - if (mRefCount[stream] != 0) { - return true; - } - if (inPastMs == 0) { - return false; - } - if (sysTime == 0) { - sysTime = systemTime(); - } - if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { - return true; - } - return false; -} - -void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle); - - dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SOURCE; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; - dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; -} - -void AudioPolicyManager::AudioOutputDescriptor::toAudioPort( - struct audio_port *port) const -{ - ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle); - mProfile->toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.mix.hw_module = mProfile->mModule->mHandle; - port->ext.mix.handle = mIoHandle; - port->ext.mix.latency_class = - mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL; -} - -status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " ID: %d\n", mId); - result.append(buffer); - snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); - result.append(buffer); - snprintf(buffer, SIZE, " Format: %08x\n", mFormat); - result.append(buffer); - snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); - result.append(buffer); - snprintf(buffer, SIZE, " Latency: %d\n", mLatency); - result.append(buffer); - snprintf(buffer, SIZE, " Flags %08x\n", mFlags); - result.append(buffer); - snprintf(buffer, SIZE, " Devices %08x\n", device()); - result.append(buffer); - snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); - result.append(buffer); - for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { - snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", - i, mCurVolume[i], mRefCount[i], mMuteCount[i]); - result.append(buffer); - } - write(fd, result.string(), result.size()); - - return NO_ERROR; -} - -// --- AudioInputDescriptor class implementation - -AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile) - : mId(0), mIoHandle(0), - mDevice(AUDIO_DEVICE_NONE), mPolicyMix(NULL), mPatchHandle(0), mRefCount(0), - mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false) -{ - if (profile != NULL) { - mSamplingRate = profile->pickSamplingRate(); - mFormat = profile->pickFormat(); - mChannelMask = profile->pickChannelMask(); - if (profile->mGains.size() > 0) { - profile->mGains[0]->getDefaultConfig(&mGain); - } - } -} - -void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - ALOG_ASSERT(mProfile != 0, - "toAudioPortConfig() called on input with null profile %d", mIoHandle); - dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| - AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = AUDIO_PORT_ROLE_SINK; - dstConfig->type = AUDIO_PORT_TYPE_MIX; - dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; - dstConfig->ext.mix.handle = mIoHandle; - dstConfig->ext.mix.usecase.source = mInputSource; -} - -void AudioPolicyManager::AudioInputDescriptor::toAudioPort( - struct audio_port *port) const -{ - ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle); - - mProfile->toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.mix.hw_module = mProfile->mModule->mHandle; - port->ext.mix.handle = mIoHandle; - port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL; -} - -status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " ID: %d\n", mId); - result.append(buffer); - snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); - result.append(buffer); - snprintf(buffer, SIZE, " Format: %d\n", mFormat); - result.append(buffer); - snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); - result.append(buffer); - snprintf(buffer, SIZE, " Devices %08x\n", mDevice); - result.append(buffer); - snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); - result.append(buffer); - snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount); - result.append(buffer); - - write(fd, result.string(), result.size()); - - return NO_ERROR; -} - -// --- StreamDescriptor class implementation - -AudioPolicyManager::StreamDescriptor::StreamDescriptor() - : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) -{ - mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); -} - -int AudioPolicyManager::StreamDescriptor::getVolumeIndex(audio_devices_t device) -{ - device = AudioPolicyManager::getDeviceForVolume(device); - // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT - if (mIndexCur.indexOfKey(device) < 0) { - device = AUDIO_DEVICE_OUT_DEFAULT; - } - return mIndexCur.valueFor(device); -} - -void AudioPolicyManager::StreamDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%s %02d %02d ", - mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); - result.append(buffer); - for (size_t i = 0; i < mIndexCur.size(); i++) { - snprintf(buffer, SIZE, "%04x : %02d, ", - mIndexCur.keyAt(i), - mIndexCur.valueAt(i)); - result.append(buffer); - } - result.append("\n"); - - write(fd, result.string(), result.size()); -} - -// --- EffectDescriptor class implementation - -status_t AudioPolicyManager::EffectDescriptor::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " I/O: %d\n", mIo); - result.append(buffer); - snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); - result.append(buffer); - snprintf(buffer, SIZE, " Session: %d\n", mSession); - result.append(buffer); - snprintf(buffer, SIZE, " Name: %s\n", mDesc.name); - result.append(buffer); - snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled"); - result.append(buffer); - write(fd, result.string(), result.size()); - - return NO_ERROR; -} - -// --- HwModule class implementation - -AudioPolicyManager::HwModule::HwModule(const char *name) - : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), - mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) -{ -} - -AudioPolicyManager::HwModule::~HwModule() -{ - for (size_t i = 0; i < mOutputProfiles.size(); i++) { - mOutputProfiles[i]->mSupportedDevices.clear(); - } - for (size_t i = 0; i < mInputProfiles.size(); i++) { - mInputProfiles[i]->mSupportedDevices.clear(); - } - free((void *)mName); -} - -status_t AudioPolicyManager::HwModule::loadInput(cnode *root) -{ - cnode *node = root->first_child; - - sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this); - - while (node) { - if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { - profile->loadSamplingRates((char *)node->value); - } else if (strcmp(node->name, FORMATS_TAG) == 0) { - profile->loadFormats((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - profile->loadInChannels((char *)node->value); - } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, - mDeclaredDevices); - } else if (strcmp(node->name, FLAGS_TAG) == 0) { - profile->mFlags = parseInputFlagNames((char *)node->value); - } else if (strcmp(node->name, GAINS_TAG) == 0) { - profile->loadGains(node); - } - node = node->next; - } - ALOGW_IF(profile->mSupportedDevices.isEmpty(), - "loadInput() invalid supported devices"); - ALOGW_IF(profile->mChannelMasks.size() == 0, - "loadInput() invalid supported channel masks"); - ALOGW_IF(profile->mSamplingRates.size() == 0, - "loadInput() invalid supported sampling rates"); - ALOGW_IF(profile->mFormats.size() == 0, - "loadInput() invalid supported formats"); - if (!profile->mSupportedDevices.isEmpty() && - (profile->mChannelMasks.size() != 0) && - (profile->mSamplingRates.size() != 0) && - (profile->mFormats.size() != 0)) { - - ALOGV("loadInput() adding input Supported Devices %04x", - profile->mSupportedDevices.types()); - - mInputProfiles.add(profile); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioPolicyManager::HwModule::loadOutput(cnode *root) -{ - cnode *node = root->first_child; - - sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this); - - while (node) { - if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) { - profile->loadSamplingRates((char *)node->value); - } else if (strcmp(node->name, FORMATS_TAG) == 0) { - profile->loadFormats((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - profile->loadOutChannels((char *)node->value); - } else if (strcmp(node->name, DEVICES_TAG) == 0) { - profile->mSupportedDevices.loadDevicesFromName((char *)node->value, - mDeclaredDevices); - } else if (strcmp(node->name, FLAGS_TAG) == 0) { - profile->mFlags = parseOutputFlagNames((char *)node->value); - } else if (strcmp(node->name, GAINS_TAG) == 0) { - profile->loadGains(node); - } - node = node->next; - } - ALOGW_IF(profile->mSupportedDevices.isEmpty(), - "loadOutput() invalid supported devices"); - ALOGW_IF(profile->mChannelMasks.size() == 0, - "loadOutput() invalid supported channel masks"); - ALOGW_IF(profile->mSamplingRates.size() == 0, - "loadOutput() invalid supported sampling rates"); - ALOGW_IF(profile->mFormats.size() == 0, - "loadOutput() invalid supported formats"); - if (!profile->mSupportedDevices.isEmpty() && - (profile->mChannelMasks.size() != 0) && - (profile->mSamplingRates.size() != 0) && - (profile->mFormats.size() != 0)) { - - ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x", - profile->mSupportedDevices.types(), profile->mFlags); - - mOutputProfiles.add(profile); - return NO_ERROR; - } else { - return BAD_VALUE; - } -} - -status_t AudioPolicyManager::HwModule::loadDevice(cnode *root) -{ - cnode *node = root->first_child; - - audio_devices_t type = AUDIO_DEVICE_NONE; - while (node) { - if (strcmp(node->name, DEVICE_TYPE) == 0) { - type = parseDeviceNames((char *)node->value); - break; - } - node = node->next; - } - if (type == AUDIO_DEVICE_NONE || - (!audio_is_input_device(type) && !audio_is_output_device(type))) { - ALOGW("loadDevice() bad type %08x", type); - return BAD_VALUE; - } - sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type); - deviceDesc->mModule = this; - - node = root->first_child; - while (node) { - if (strcmp(node->name, DEVICE_ADDRESS) == 0) { - deviceDesc->mAddress = String8((char *)node->value); - } else if (strcmp(node->name, CHANNELS_TAG) == 0) { - if (audio_is_input_device(type)) { - deviceDesc->loadInChannels((char *)node->value); - } else { - deviceDesc->loadOutChannels((char *)node->value); - } - } else if (strcmp(node->name, GAINS_TAG) == 0) { - deviceDesc->loadGains(node); - } - node = node->next; - } - - ALOGV("loadDevice() adding device name %s type %08x address %s", - deviceDesc->mName.string(), type, deviceDesc->mAddress.string()); - - mDeclaredDevices.add(deviceDesc); - - return NO_ERROR; -} - -status_t AudioPolicyManager::HwModule::addOutputProfile(String8 name, const audio_config_t *config, - audio_devices_t device, String8 address) -{ - sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SOURCE, this); - - profile->mSamplingRates.add(config->sample_rate); - profile->mChannelMasks.add(config->channel_mask); - profile->mFormats.add(config->format); - - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = address; - profile->mSupportedDevices.add(devDesc); - - mOutputProfiles.add(profile); - - return NO_ERROR; -} - -status_t AudioPolicyManager::HwModule::removeOutputProfile(String8 name) -{ - for (size_t i = 0; i < mOutputProfiles.size(); i++) { - if (mOutputProfiles[i]->mName == name) { - mOutputProfiles.removeAt(i); - break; - } - } - - return NO_ERROR; -} - -status_t AudioPolicyManager::HwModule::addInputProfile(String8 name, const audio_config_t *config, - audio_devices_t device, String8 address) -{ - sp<IOProfile> profile = new IOProfile(name, AUDIO_PORT_ROLE_SINK, this); - - profile->mSamplingRates.add(config->sample_rate); - profile->mChannelMasks.add(config->channel_mask); - profile->mFormats.add(config->format); - - sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); - devDesc->mAddress = address; - profile->mSupportedDevices.add(devDesc); - - ALOGV("addInputProfile() name %s rate %d mask 0x08", name.string(), config->sample_rate, config->channel_mask); - - mInputProfiles.add(profile); - - return NO_ERROR; -} - -status_t AudioPolicyManager::HwModule::removeInputProfile(String8 name) -{ - for (size_t i = 0; i < mInputProfiles.size(); i++) { - if (mInputProfiles[i]->mName == name) { - mInputProfiles.removeAt(i); - break; - } - } - - return NO_ERROR; -} - - -void AudioPolicyManager::HwModule::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, " - name: %s\n", mName); - result.append(buffer); - snprintf(buffer, SIZE, " - handle: %d\n", mHandle); - result.append(buffer); - snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); - result.append(buffer); - write(fd, result.string(), result.size()); - if (mOutputProfiles.size()) { - write(fd, " - outputs:\n", strlen(" - outputs:\n")); - for (size_t i = 0; i < mOutputProfiles.size(); i++) { - snprintf(buffer, SIZE, " output %zu:\n", i); - write(fd, buffer, strlen(buffer)); - mOutputProfiles[i]->dump(fd); - } - } - if (mInputProfiles.size()) { - write(fd, " - inputs:\n", strlen(" - inputs:\n")); - for (size_t i = 0; i < mInputProfiles.size(); i++) { - snprintf(buffer, SIZE, " input %zu:\n", i); - write(fd, buffer, strlen(buffer)); - mInputProfiles[i]->dump(fd); - } - } - if (mDeclaredDevices.size()) { - write(fd, " - devices:\n", strlen(" - devices:\n")); - for (size_t i = 0; i < mDeclaredDevices.size(); i++) { - mDeclaredDevices[i]->dump(fd, 4, i); - } - } -} - -// --- AudioPort class implementation - - -AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type, - audio_port_role_t role, const sp<HwModule>& module) : - mName(name), mType(type), mRole(role), mModule(module), mFlags(0) -{ - mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || - ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); -} - -void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const -{ - port->role = mRole; - port->type = mType; - unsigned int i; - for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { - if (mSamplingRates[i] != 0) { - port->sample_rates[i] = mSamplingRates[i]; - } - } - port->num_sample_rates = i; - for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { - if (mChannelMasks[i] != 0) { - port->channel_masks[i] = mChannelMasks[i]; - } - } - port->num_channel_masks = i; - for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { - if (mFormats[i] != 0) { - port->formats[i] = mFormats[i]; - } - } - port->num_formats = i; - - ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); - - for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { - port->gains[i] = mGains[i]->mGain; - } - port->num_gains = i; -} - -void AudioPolicyManager::AudioPort::importAudioPort(const sp<AudioPort> port) { - for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { - const uint32_t rate = port->mSamplingRates.itemAt(k); - if (rate != 0) { // skip "dynamic" rates - bool hasRate = false; - for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { - if (rate == mSamplingRates.itemAt(l)) { - hasRate = true; - break; - } - } - if (!hasRate) { // never import a sampling rate twice - mSamplingRates.add(rate); - } - } - } - for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { - const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); - if (mask != 0) { // skip "dynamic" masks - bool hasMask = false; - for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { - if (mask == mChannelMasks.itemAt(l)) { - hasMask = true; - break; - } - } - if (!hasMask) { // never import a channel mask twice - mChannelMasks.add(mask); - } - } - } - for (size_t k = 0 ; k < port->mFormats.size() ; k++) { - const audio_format_t format = port->mFormats.itemAt(k); - if (format != 0) { // skip "dynamic" formats - bool hasFormat = false; - for (size_t l = 0 ; l < mFormats.size() ; l++) { - if (format == mFormats.itemAt(l)) { - hasFormat = true; - break; - } - } - if (!hasFormat) { // never import a channel mask twice - mFormats.add(format); - } - } - } - for (size_t k = 0 ; k < port->mGains.size() ; k++) { - sp<AudioGain> gain = port->mGains.itemAt(k); - if (gain != 0) { - bool hasGain = false; - for (size_t l = 0 ; l < mGains.size() ; l++) { - if (gain == mGains.itemAt(l)) { - hasGain = true; - break; - } - } - if (!hasGain) { // never import a gain twice - mGains.add(gain); - } - } - } -} - -void AudioPolicyManager::AudioPort::clearCapabilities() { - mChannelMasks.clear(); - mFormats.clear(); - mSamplingRates.clear(); - mGains.clear(); -} - -void AudioPolicyManager::AudioPort::loadSamplingRates(char *name) -{ - char *str = strtok(name, "|"); - - // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling - // rates should be read from the output stream after it is opened for the first time - if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { - mSamplingRates.add(0); - return; - } - - while (str != NULL) { - uint32_t rate = atoi(str); - if (rate != 0) { - ALOGV("loadSamplingRates() adding rate %d", rate); - mSamplingRates.add(rate); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadFormats(char *name) -{ - char *str = strtok(name, "|"); - - // by convention, "0' in the first entry in mFormats indicates the supported formats - // should be read from the output stream after it is opened for the first time - if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { - mFormats.add(AUDIO_FORMAT_DEFAULT); - return; - } - - while (str != NULL) { - audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable, - ARRAY_SIZE(sFormatNameToEnumTable), - str); - if (format != AUDIO_FORMAT_DEFAULT) { - mFormats.add(format); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadInChannels(char *name) -{ - const char *str = strtok(name, "|"); - - ALOGV("loadInChannels() %s", name); - - if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { - mChannelMasks.add(0); - return; - } - - while (str != NULL) { - audio_channel_mask_t channelMask = - (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, - ARRAY_SIZE(sInChannelsNameToEnumTable), - str); - if (channelMask != 0) { - ALOGV("loadInChannels() adding channelMask %04x", channelMask); - mChannelMasks.add(channelMask); - } - str = strtok(NULL, "|"); - } -} - -void AudioPolicyManager::AudioPort::loadOutChannels(char *name) -{ - const char *str = strtok(name, "|"); - - ALOGV("loadOutChannels() %s", name); - - // by convention, "0' in the first entry in mChannelMasks indicates the supported channel - // masks should be read from the output stream after it is opened for the first time - if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { - mChannelMasks.add(0); - return; - } - - while (str != NULL) { - audio_channel_mask_t channelMask = - (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, - ARRAY_SIZE(sOutChannelsNameToEnumTable), - str); - if (channelMask != 0) { - mChannelMasks.add(channelMask); - } - str = strtok(NULL, "|"); - } - return; -} - -audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name) -{ - const char *str = strtok(name, "|"); - - ALOGV("loadGainMode() %s", name); - audio_gain_mode_t mode = 0; - while (str != NULL) { - mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable, - ARRAY_SIZE(sGainModeNameToEnumTable), - str); - str = strtok(NULL, "|"); - } - return mode; -} - -void AudioPolicyManager::AudioPort::loadGain(cnode *root, int index) -{ - cnode *node = root->first_child; - - sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); - - while (node) { - if (strcmp(node->name, GAIN_MODE) == 0) { - gain->mGain.mode = loadGainMode((char *)node->value); - } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { - if (mUseInChannelMask) { - gain->mGain.channel_mask = - (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, - ARRAY_SIZE(sInChannelsNameToEnumTable), - (char *)node->value); - } else { - gain->mGain.channel_mask = - (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, - ARRAY_SIZE(sOutChannelsNameToEnumTable), - (char *)node->value); - } - } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { - gain->mGain.min_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { - gain->mGain.max_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { - gain->mGain.default_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { - gain->mGain.step_value = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { - gain->mGain.min_ramp_ms = atoi((char *)node->value); - } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { - gain->mGain.max_ramp_ms = atoi((char *)node->value); - } - node = node->next; - } - - ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", - gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value); - - if (gain->mGain.mode == 0) { - return; - } - mGains.add(gain); -} - -void AudioPolicyManager::AudioPort::loadGains(cnode *root) -{ - cnode *node = root->first_child; - int index = 0; - while (node) { - ALOGV("loadGains() loading gain %s", node->name); - loadGain(node, index++); - node = node->next; - } -} - -status_t AudioPolicyManager::AudioPort::checkExactSamplingRate(uint32_t samplingRate) const -{ - if (mSamplingRates.isEmpty()) { - return NO_ERROR; - } - - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if (mSamplingRates[i] == samplingRate) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, - uint32_t *updatedSamplingRate) const -{ - if (mSamplingRates.isEmpty()) { - return NO_ERROR; - } - - // Search for the closest supported sampling rate that is above (preferred) - // or below (acceptable) the desired sampling rate, within a permitted ratio. - // The sampling rates do not need to be sorted in ascending order. - ssize_t maxBelow = -1; - ssize_t minAbove = -1; - uint32_t candidate; - for (size_t i = 0; i < mSamplingRates.size(); i++) { - candidate = mSamplingRates[i]; - if (candidate == samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - // candidate < desired - if (candidate < samplingRate) { - if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { - maxBelow = i; - } - // candidate > desired - } else { - if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { - minAbove = i; - } - } - } - // This uses hard-coded knowledge about AudioFlinger resampling ratios. - // TODO Move these assumptions out. - static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs - static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur - // due to approximation by an int32_t of the - // phase increments - // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. - if (minAbove >= 0) { - candidate = mSamplingRates[minAbove]; - if (candidate / kMaxDownSampleRatio <= samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - } - // But if we have to up-sample from a lower sampling rate, that's OK. - if (maxBelow >= 0) { - candidate = mSamplingRates[maxBelow]; - if (candidate * kMaxUpSampleRatio >= samplingRate) { - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = candidate; - } - return NO_ERROR; - } - } - // leave updatedSamplingRate unmodified - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const -{ - if (mChannelMasks.isEmpty()) { - return NO_ERROR; - } - - for (size_t i = 0; i < mChannelMasks.size(); i++) { - if (mChannelMasks[i] == channelMask) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) - const -{ - if (mChannelMasks.isEmpty()) { - return NO_ERROR; - } - - const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - // FIXME Does not handle multi-channel automatic conversions yet - audio_channel_mask_t supported = mChannelMasks[i]; - if (supported == channelMask) { - return NO_ERROR; - } - if (isRecordThread) { - // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. - // FIXME Abstract this out to a table. - if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) - && channelMask == AUDIO_CHANNEL_IN_MONO) || - (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK - || channelMask == AUDIO_CHANNEL_IN_STEREO))) { - return NO_ERROR; - } - } - } - return BAD_VALUE; -} - -status_t AudioPolicyManager::AudioPort::checkFormat(audio_format_t format) const -{ - if (mFormats.isEmpty()) { - return NO_ERROR; - } - - for (size_t i = 0; i < mFormats.size(); i ++) { - if (mFormats[i] == format) { - return NO_ERROR; - } - } - return BAD_VALUE; -} - - -uint32_t AudioPolicyManager::AudioPort::pickSamplingRate() const -{ - // special case for uninitialized dynamic profile - if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { - return 0; - } - - // For direct outputs, pick minimum sampling rate: this helps ensuring that the - // channel count / sampling rate combination chosen will be supported by the connected - // sink - if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && - (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { - uint32_t samplingRate = UINT_MAX; - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { - samplingRate = mSamplingRates[i]; - } - } - return (samplingRate == UINT_MAX) ? 0 : samplingRate; - } - - uint32_t samplingRate = 0; - uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; - - // For mixed output and inputs, use max mixer sampling rates. Do not - // limit sampling rate otherwise - if (mType != AUDIO_PORT_TYPE_MIX) { - maxRate = UINT_MAX; - } - for (size_t i = 0; i < mSamplingRates.size(); i ++) { - if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { - samplingRate = mSamplingRates[i]; - } - } - return samplingRate; -} - -audio_channel_mask_t AudioPolicyManager::AudioPort::pickChannelMask() const -{ - // special case for uninitialized dynamic profile - if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { - return AUDIO_CHANNEL_NONE; - } - audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE; - - // For direct outputs, pick minimum channel count: this helps ensuring that the - // channel count / sampling rate combination chosen will be supported by the connected - // sink - if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && - (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { - uint32_t channelCount = UINT_MAX; - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - uint32_t cnlCount; - if (mUseInChannelMask) { - cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); - } else { - cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); - } - if ((cnlCount < channelCount) && (cnlCount > 0)) { - channelMask = mChannelMasks[i]; - channelCount = cnlCount; - } - } - return channelMask; - } - - uint32_t channelCount = 0; - uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; - - // For mixed output and inputs, use max mixer channel count. Do not - // limit channel count otherwise - if (mType != AUDIO_PORT_TYPE_MIX) { - maxCount = UINT_MAX; - } - for (size_t i = 0; i < mChannelMasks.size(); i ++) { - uint32_t cnlCount; - if (mUseInChannelMask) { - cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); - } else { - cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); - } - if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { - channelMask = mChannelMasks[i]; - channelCount = cnlCount; - } - } - return channelMask; -} - -/* format in order of increasing preference */ -const audio_format_t AudioPolicyManager::AudioPort::sPcmFormatCompareTable[] = { - AUDIO_FORMAT_DEFAULT, - AUDIO_FORMAT_PCM_16_BIT, - AUDIO_FORMAT_PCM_8_24_BIT, - AUDIO_FORMAT_PCM_24_BIT_PACKED, - AUDIO_FORMAT_PCM_32_BIT, - AUDIO_FORMAT_PCM_FLOAT, -}; - -int AudioPolicyManager::AudioPort::compareFormats(audio_format_t format1, - audio_format_t format2) -{ - // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any - // compressed format and better than any PCM format. This is by design of pickFormat() - if (!audio_is_linear_pcm(format1)) { - if (!audio_is_linear_pcm(format2)) { - return 0; - } - return 1; - } - if (!audio_is_linear_pcm(format2)) { - return -1; - } - - int index1 = -1, index2 = -1; - for (size_t i = 0; - (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); - i ++) { - if (sPcmFormatCompareTable[i] == format1) { - index1 = i; - } - if (sPcmFormatCompareTable[i] == format2) { - index2 = i; - } - } - // format1 not found => index1 < 0 => format2 > format1 - // format2 not found => index2 < 0 => format2 < format1 - return index1 - index2; -} - -audio_format_t AudioPolicyManager::AudioPort::pickFormat() const -{ - // special case for uninitialized dynamic profile - if (mFormats.size() == 1 && mFormats[0] == 0) { - return AUDIO_FORMAT_DEFAULT; - } - - audio_format_t format = AUDIO_FORMAT_DEFAULT; - audio_format_t bestFormat = - AudioPolicyManager::AudioPort::sPcmFormatCompareTable[ - ARRAY_SIZE(AudioPolicyManager::AudioPort::sPcmFormatCompareTable) - 1]; - // For mixed output and inputs, use best mixer output format. Do not - // limit format otherwise - if ((mType != AUDIO_PORT_TYPE_MIX) || - ((mRole == AUDIO_PORT_ROLE_SOURCE) && - (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { - bestFormat = AUDIO_FORMAT_INVALID; - } - - for (size_t i = 0; i < mFormats.size(); i ++) { - if ((compareFormats(mFormats[i], format) > 0) && - (compareFormats(mFormats[i], bestFormat) <= 0)) { - format = mFormats[i]; - } - } - return format; -} - -status_t AudioPolicyManager::AudioPort::checkGain(const struct audio_gain_config *gainConfig, - int index) const -{ - if (index < 0 || (size_t)index >= mGains.size()) { - return BAD_VALUE; - } - return mGains[index]->checkConfig(gainConfig); -} - -void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - if (mName.size() != 0) { - snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); - result.append(buffer); - } - - if (mSamplingRates.size() != 0) { - snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mSamplingRates.size(); i++) { - if (i == 0 && mSamplingRates[i] == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "%d", mSamplingRates[i]); - } - result.append(buffer); - result.append(i == (mSamplingRates.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - - if (mChannelMasks.size() != 0) { - snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mChannelMasks.size(); i++) { - ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); - - if (i == 0 && mChannelMasks[i] == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); - } - result.append(buffer); - result.append(i == (mChannelMasks.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - - if (mFormats.size() != 0) { - snprintf(buffer, SIZE, "%*s- formats: ", spaces, ""); - result.append(buffer); - for (size_t i = 0; i < mFormats.size(); i++) { - const char *formatStr = enumToString(sFormatNameToEnumTable, - ARRAY_SIZE(sFormatNameToEnumTable), - mFormats[i]); - if (i == 0 && strcmp(formatStr, "") == 0) { - snprintf(buffer, SIZE, "Dynamic"); - } else { - snprintf(buffer, SIZE, "%s", formatStr); - } - result.append(buffer); - result.append(i == (mFormats.size() - 1) ? "" : ", "); - } - result.append("\n"); - } - write(fd, result.string(), result.size()); - if (mGains.size() != 0) { - snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); - write(fd, buffer, strlen(buffer) + 1); - result.append(buffer); - for (size_t i = 0; i < mGains.size(); i++) { - mGains[i]->dump(fd, spaces + 2, i); - } - } -} - -// --- AudioGain class implementation - -AudioPolicyManager::AudioGain::AudioGain(int index, bool useInChannelMask) -{ - mIndex = index; - mUseInChannelMask = useInChannelMask; - memset(&mGain, 0, sizeof(struct audio_gain)); -} - -void AudioPolicyManager::AudioGain::getDefaultConfig(struct audio_gain_config *config) -{ - config->index = mIndex; - config->mode = mGain.mode; - config->channel_mask = mGain.channel_mask; - if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { - config->values[0] = mGain.default_value; - } else { - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(mGain.channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(mGain.channel_mask); - } - for (size_t i = 0; i < numValues; i++) { - config->values[i] = mGain.default_value; - } - } - if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { - config->ramp_duration_ms = mGain.min_ramp_ms; - } -} - -status_t AudioPolicyManager::AudioGain::checkConfig(const struct audio_gain_config *config) -{ - if ((config->mode & ~mGain.mode) != 0) { - return BAD_VALUE; - } - if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { - if ((config->values[0] < mGain.min_value) || - (config->values[0] > mGain.max_value)) { - return BAD_VALUE; - } - } else { - if ((config->channel_mask & ~mGain.channel_mask) != 0) { - return BAD_VALUE; - } - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(config->channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(config->channel_mask); - } - for (size_t i = 0; i < numValues; i++) { - if ((config->values[i] < mGain.min_value) || - (config->values[i] > mGain.max_value)) { - return BAD_VALUE; - } - } - } - if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { - if ((config->ramp_duration_ms < mGain.min_ramp_ms) || - (config->ramp_duration_ms > mGain.max_ramp_ms)) { - return BAD_VALUE; - } - } - return NO_ERROR; -} - -void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms); - result.append(buffer); - - write(fd, result.string(), result.size()); -} - -// --- AudioPortConfig class implementation - -AudioPolicyManager::AudioPortConfig::AudioPortConfig() -{ - mSamplingRate = 0; - mChannelMask = AUDIO_CHANNEL_NONE; - mFormat = AUDIO_FORMAT_INVALID; - mGain.index = -1; -} - -status_t AudioPolicyManager::AudioPortConfig::applyAudioPortConfig( - const struct audio_port_config *config, - struct audio_port_config *backupConfig) -{ - struct audio_port_config localBackupConfig; - status_t status = NO_ERROR; - - localBackupConfig.config_mask = config->config_mask; - toAudioPortConfig(&localBackupConfig); - - sp<AudioPort> audioport = getAudioPort(); - if (audioport == 0) { - status = NO_INIT; - goto exit; - } - if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - status = audioport->checkExactSamplingRate(config->sample_rate); - if (status != NO_ERROR) { - goto exit; - } - mSamplingRate = config->sample_rate; - } - if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - status = audioport->checkExactChannelMask(config->channel_mask); - if (status != NO_ERROR) { - goto exit; - } - mChannelMask = config->channel_mask; - } - if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - status = audioport->checkFormat(config->format); - if (status != NO_ERROR) { - goto exit; - } - mFormat = config->format; - } - if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { - status = audioport->checkGain(&config->gain, config->gain.index); - if (status != NO_ERROR) { - goto exit; - } - mGain = config->gain; - } - -exit: - if (status != NO_ERROR) { - applyAudioPortConfig(&localBackupConfig); - } - if (backupConfig != NULL) { - *backupConfig = localBackupConfig; - } - return status; -} - -void AudioPolicyManager::AudioPortConfig::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - dstConfig->sample_rate = mSamplingRate; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { - dstConfig->sample_rate = srcConfig->sample_rate; - } - } else { - dstConfig->sample_rate = 0; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - dstConfig->channel_mask = mChannelMask; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { - dstConfig->channel_mask = srcConfig->channel_mask; - } - } else { - dstConfig->channel_mask = AUDIO_CHANNEL_NONE; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { - dstConfig->format = mFormat; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { - dstConfig->format = srcConfig->format; - } - } else { - dstConfig->format = AUDIO_FORMAT_INVALID; - } - if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { - dstConfig->gain = mGain; - if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { - dstConfig->gain = srcConfig->gain; - } - } else { - dstConfig->gain.index = -1; - } - if (dstConfig->gain.index != -1) { - dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; - } else { - dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; - } -} - -// --- IOProfile class implementation - -AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role, - const sp<HwModule>& module) - : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module) -{ -} - -AudioPolicyManager::IOProfile::~IOProfile() -{ -} - -// checks if the IO profile is compatible with specified parameters. -// Sampling rate, format and channel mask must be specified in order to -// get a valid a match -bool AudioPolicyManager::IOProfile::isCompatibleProfile(audio_devices_t device, - String8 address, - uint32_t samplingRate, - uint32_t *updatedSamplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - uint32_t flags) const -{ - const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE; - const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; - ALOG_ASSERT(isPlaybackThread != isRecordThread); - - if (device != AUDIO_DEVICE_NONE && mSupportedDevices.getDevice(device, address) == 0) { - return false; - } - - if (samplingRate == 0) { - return false; - } - uint32_t myUpdatedSamplingRate = samplingRate; - if (isPlaybackThread && checkExactSamplingRate(samplingRate) != NO_ERROR) { - return false; - } - if (isRecordThread && checkCompatibleSamplingRate(samplingRate, &myUpdatedSamplingRate) != - NO_ERROR) { - return false; - } - - if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) { - return false; - } - - if (isPlaybackThread && (!audio_is_output_channel(channelMask) || - checkExactChannelMask(channelMask) != NO_ERROR)) { - return false; - } - if (isRecordThread && (!audio_is_input_channel(channelMask) || - checkCompatibleChannelMask(channelMask) != NO_ERROR)) { - return false; - } - - if (isPlaybackThread && (mFlags & flags) != flags) { - return false; - } - // The only input flag that is allowed to be different is the fast flag. - // An existing fast stream is compatible with a normal track request. - // An existing normal stream is compatible with a fast track request, - // but the fast request will be denied by AudioFlinger and converted to normal track. - if (isRecordThread && ((mFlags ^ flags) & - ~AUDIO_INPUT_FLAG_FAST)) { - return false; - } - - if (updatedSamplingRate != NULL) { - *updatedSamplingRate = myUpdatedSamplingRate; - } - return true; -} - -void AudioPolicyManager::IOProfile::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - AudioPort::dump(fd, 4); - - snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); - result.append(buffer); - snprintf(buffer, SIZE, " - devices:\n"); - result.append(buffer); - write(fd, result.string(), result.size()); - for (size_t i = 0; i < mSupportedDevices.size(); i++) { - mSupportedDevices[i]->dump(fd, 6, i); - } -} - -void AudioPolicyManager::IOProfile::log() -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - ALOGV(" - sampling rates: "); - for (size_t i = 0; i < mSamplingRates.size(); i++) { - ALOGV(" %d", mSamplingRates[i]); - } - - ALOGV(" - channel masks: "); - for (size_t i = 0; i < mChannelMasks.size(); i++) { - ALOGV(" 0x%04x", mChannelMasks[i]); - } - - ALOGV(" - formats: "); - for (size_t i = 0; i < mFormats.size(); i++) { - ALOGV(" 0x%08x", mFormats[i]); - } - - ALOGV(" - devices: 0x%04x\n", mSupportedDevices.types()); - ALOGV(" - flags: 0x%04x\n", mFlags); -} - - -// --- DeviceDescriptor implementation - - -AudioPolicyManager::DeviceDescriptor::DeviceDescriptor(const String8& name, audio_devices_t type) : - AudioPort(name, AUDIO_PORT_TYPE_DEVICE, - audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK : - AUDIO_PORT_ROLE_SOURCE, - NULL), - mDeviceType(type), mAddress(""), mId(0) -{ -} - -bool AudioPolicyManager::DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const -{ - // Devices are considered equal if they: - // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE) - // - have the same address or one device does not specify the address - // - have the same channel mask or one device does not specify the channel mask - return (mDeviceType == other->mDeviceType) && - (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && - (mChannelMask == 0 || other->mChannelMask == 0 || - mChannelMask == other->mChannelMask); -} - -void AudioPolicyManager::DeviceDescriptor::loadGains(cnode *root) -{ - AudioPort::loadGains(root); - if (mGains.size() > 0) { - mGains[0]->getDefaultConfig(&mGain); - } -} - - -void AudioPolicyManager::DeviceVector::refreshTypes() -{ - mDeviceTypes = AUDIO_DEVICE_NONE; - for(size_t i = 0; i < size(); i++) { - mDeviceTypes |= itemAt(i)->mDeviceType; - } - ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes); -} - -ssize_t AudioPolicyManager::DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const -{ - for(size_t i = 0; i < size(); i++) { - if (item->equals(itemAt(i))) { - return i; - } - } - return -1; -} - -ssize_t AudioPolicyManager::DeviceVector::add(const sp<DeviceDescriptor>& item) -{ - ssize_t ret = indexOf(item); - - if (ret < 0) { - ret = SortedVector::add(item); - if (ret >= 0) { - refreshTypes(); - } - } else { - ALOGW("DeviceVector::add device %08x already in", item->mDeviceType); - ret = -1; - } - return ret; -} - -ssize_t AudioPolicyManager::DeviceVector::remove(const sp<DeviceDescriptor>& item) -{ - size_t i; - ssize_t ret = indexOf(item); - - if (ret < 0) { - ALOGW("DeviceVector::remove device %08x not in", item->mDeviceType); - } else { - ret = SortedVector::removeAt(ret); - if (ret >= 0) { - refreshTypes(); - } - } - return ret; -} - -void AudioPolicyManager::DeviceVector::loadDevicesFromType(audio_devices_t types) -{ - DeviceVector deviceList; - - uint32_t role_bit = AUDIO_DEVICE_BIT_IN & types; - types &= ~role_bit; - - while (types) { - uint32_t i = 31 - __builtin_clz(types); - uint32_t type = 1 << i; - types &= ~type; - add(new DeviceDescriptor(String8(""), type | role_bit)); - } -} - -void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name, - const DeviceVector& declaredDevices) -{ - char *devName = strtok(name, "|"); - while (devName != NULL) { - if (strlen(devName) != 0) { - audio_devices_t type = stringToEnum(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - devName); - if (type != AUDIO_DEVICE_NONE) { - sp<DeviceDescriptor> dev = new DeviceDescriptor(String8(""), type); - if (type == AUDIO_DEVICE_IN_REMOTE_SUBMIX || - type == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ) { - dev->mAddress = String8("0"); - } - add(dev); - } else { - sp<DeviceDescriptor> deviceDesc = - declaredDevices.getDeviceFromName(String8(devName)); - if (deviceDesc != 0) { - add(deviceDesc); - } - } - } - devName = strtok(NULL, "|"); - } -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice( - audio_devices_t type, String8 address) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mDeviceType == type) { - if (address == "" || itemAt(i)->mAddress == address) { - device = itemAt(i); - if (itemAt(i)->mAddress == address) { - break; - } - } - } - } - ALOGV("DeviceVector::getDevice() for type %08x address %s found %p", - type, address.string(), device.get()); - return device; -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId( - audio_port_handle_t id) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%zu)->mId %d", id, i, itemAt(i)->mId); - if (itemAt(i)->mId == id) { - device = itemAt(i); - break; - } - } - return device; -} - -AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType( - audio_devices_t type) const -{ - DeviceVector devices; - bool isOutput = audio_is_output_devices(type); - type &= ~AUDIO_DEVICE_BIT_IN; - for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { - bool curIsOutput = audio_is_output_devices(itemAt(i)->mDeviceType); - audio_devices_t curType = itemAt(i)->mDeviceType & ~AUDIO_DEVICE_BIT_IN; - if ((isOutput == curIsOutput) && ((type & curType) != 0)) { - devices.add(itemAt(i)); - type &= ~curType; - ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", - itemAt(i)->mDeviceType, itemAt(i).get()); - } - } - return devices; -} - -AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromTypeAddr( - audio_devices_t type, String8 address) const -{ - DeviceVector devices; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mDeviceType == type) { - if (itemAt(i)->mAddress == address) { - devices.add(itemAt(i)); - } - } - } - return devices; -} - -sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName( - const String8& name) const -{ - sp<DeviceDescriptor> device; - for (size_t i = 0; i < size(); i++) { - if (itemAt(i)->mName == name) { - device = itemAt(i); - break; - } - } - return device; -} - -void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig( - struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig) const -{ - dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; - if (srcConfig != NULL) { - dstConfig->config_mask |= srcConfig->config_mask; - } - - AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); - - dstConfig->id = mId; - dstConfig->role = audio_is_output_device(mDeviceType) ? - AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; - dstConfig->type = AUDIO_PORT_TYPE_DEVICE; - dstConfig->ext.device.type = mDeviceType; - dstConfig->ext.device.hw_module = mModule->mHandle; - strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); -} - -void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const -{ - ALOGV("DeviceDescriptor::toAudioPort() handle %d type %x", mId, mDeviceType); - AudioPort::toAudioPort(port); - port->id = mId; - toAudioPortConfig(&port->active_config); - port->ext.device.type = mDeviceType; - port->ext.device.hw_module = mModule->mHandle; - strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN); -} - -status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1); - result.append(buffer); - if (mId != 0) { - snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId); - result.append(buffer); - } - snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "", - enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mDeviceType)); - result.append(buffer); - if (mAddress.size() != 0) { - snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string()); - result.append(buffer); - } - write(fd, result.string(), result.size()); - AudioPort::dump(fd, spaces); - - return NO_ERROR; -} - -status_t AudioPolicyManager::AudioPatch::dump(int fd, int spaces, int index) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - - snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); - result.append(buffer); - snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sources; i++) { - if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sources[i].id, enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mPatch.sources[i].ext.device.type)); - } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); - } - result.append(buffer); - } - snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); - result.append(buffer); - for (size_t i = 0; i < mPatch.num_sinks; i++) { - if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { - snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", - mPatch.sinks[i].id, enumToString(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - mPatch.sinks[i].ext.device.type)); - } else { - snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", - mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); - } - result.append(buffer); - } - - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -// --- audio_policy.conf file parsing - -uint32_t AudioPolicyManager::parseOutputFlagNames(char *name) -{ - uint32_t flag = 0; - - // it is OK to cast name to non const here as we are not going to use it after - // strtok() modifies it - char *flagName = strtok(name, "|"); - while (flagName != NULL) { - if (strlen(flagName) != 0) { - flag |= stringToEnum(sOutputFlagNameToEnumTable, - ARRAY_SIZE(sOutputFlagNameToEnumTable), - flagName); - } - flagName = strtok(NULL, "|"); - } - //force direct flag if offload flag is set: offloading implies a direct output stream - // and all common behaviors are driven by checking only the direct flag - // this should normally be set appropriately in the policy configuration file - if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { - flag |= AUDIO_OUTPUT_FLAG_DIRECT; - } - - return flag; -} - -uint32_t AudioPolicyManager::parseInputFlagNames(char *name) -{ - uint32_t flag = 0; - - // it is OK to cast name to non const here as we are not going to use it after - // strtok() modifies it - char *flagName = strtok(name, "|"); - while (flagName != NULL) { - if (strlen(flagName) != 0) { - flag |= stringToEnum(sInputFlagNameToEnumTable, - ARRAY_SIZE(sInputFlagNameToEnumTable), - flagName); - } - flagName = strtok(NULL, "|"); - } - return flag; -} - -audio_devices_t AudioPolicyManager::parseDeviceNames(char *name) -{ - uint32_t device = 0; - - char *devName = strtok(name, "|"); - while (devName != NULL) { - if (strlen(devName) != 0) { - device |= stringToEnum(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - devName); - } - devName = strtok(NULL, "|"); - } - return device; -} - -void AudioPolicyManager::loadHwModule(cnode *root) -{ - status_t status = NAME_NOT_FOUND; - cnode *node; - sp<HwModule> module = new HwModule(root->name); - - node = config_find(root, DEVICES_TAG); - if (node != NULL) { - node = node->first_child; - while (node) { - ALOGV("loadHwModule() loading device %s", node->name); - status_t tmpStatus = module->loadDevice(node); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - } - node = config_find(root, OUTPUTS_TAG); - if (node != NULL) { - node = node->first_child; - while (node) { - ALOGV("loadHwModule() loading output %s", node->name); - status_t tmpStatus = module->loadOutput(node); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - } - node = config_find(root, INPUTS_TAG); - if (node != NULL) { - node = node->first_child; - while (node) { - ALOGV("loadHwModule() loading input %s", node->name); - status_t tmpStatus = module->loadInput(node); - if (status == NAME_NOT_FOUND || status == NO_ERROR) { - status = tmpStatus; - } - node = node->next; - } - } - loadGlobalConfig(root, module); - - if (status == NO_ERROR) { - mHwModules.add(module); - } -} - -void AudioPolicyManager::loadHwModules(cnode *root) -{ - cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); - if (node == NULL) { - return; - } - - node = node->first_child; - while (node) { - ALOGV("loadHwModules() loading module %s", node->name); - loadHwModule(node); - node = node->next; - } -} - -void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<HwModule>& module) -{ - cnode *node = config_find(root, GLOBAL_CONFIG_TAG); - - if (node == NULL) { - return; - } - DeviceVector declaredDevices; - if (module != NULL) { - declaredDevices = module->mDeclaredDevices; - } - - node = node->first_child; - while (node) { - if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) { - mAvailableOutputDevices.loadDevicesFromName((char *)node->value, - declaredDevices); - ALOGV("loadGlobalConfig() Attached Output Devices %08x", - mAvailableOutputDevices.types()); - } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { - audio_devices_t device = (audio_devices_t)stringToEnum(sDeviceNameToEnumTable, - ARRAY_SIZE(sDeviceNameToEnumTable), - (char *)node->value); - if (device != AUDIO_DEVICE_NONE) { - mDefaultOutputDevice = new DeviceDescriptor(String8(""), device); - } else { - ALOGW("loadGlobalConfig() default device not specified"); - } - ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mDeviceType); - } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) { - mAvailableInputDevices.loadDevicesFromName((char *)node->value, - declaredDevices); - ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types()); - } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) { - mSpeakerDrcEnabled = stringToBool((char *)node->value); - ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); - } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { - uint32_t major, minor; - sscanf((char *)node->value, "%u.%u", &major, &minor); - module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor); - ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u", - module->mHalVersion, major, minor); - } - node = node->next; - } -} - -status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path) -{ - cnode *root; - char *data; - - data = (char *)load_file(path, NULL); - if (data == NULL) { - return -ENODEV; - } - root = config_node("", ""); - config_load(root, data); - - loadHwModules(root); - // legacy audio_policy.conf files have one global_configuration section - loadGlobalConfig(root, getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)); - config_free(root); - free(root); - free(data); - - ALOGI("loadAudioPolicyConfig() loaded %s\n", path); - - return NO_ERROR; -} - void AudioPolicyManager::defaultAudioPolicyConfig(void) { sp<HwModule> module; sp<IOProfile> profile; - sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), - AUDIO_DEVICE_IN_BUILTIN_MIC); + sp<DeviceDescriptor> defaultInputDevice = + new DeviceDescriptor(String8("builtin-mic"), AUDIO_DEVICE_IN_BUILTIN_MIC); mAvailableOutputDevices.add(mDefaultOutputDevice); mAvailableInputDevices.add(defaultInputDevice); @@ -8064,7 +4491,8 @@ audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_ } } -bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) { +bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) +{ // has flags that map to a strategy? if ((paa->flags & (AUDIO_FLAG_AUDIBILITY_ENFORCED | AUDIO_FLAG_SCO | AUDIO_FLAG_BEACON)) != 0) { return true; @@ -8095,4 +4523,39 @@ bool AudioPolicyManager::isValidAttributes(const audio_attributes_t *paa) { return true; } +bool AudioPolicyManager::isStrategyActive(const sp<AudioOutputDescriptor> outputDesc, + routing_strategy strategy, uint32_t inPastMs, + nsecs_t sysTime) const +{ + if ((sysTime == 0) && (inPastMs != 0)) { + sysTime = systemTime(); + } + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + if (i == AUDIO_STREAM_PATCH) { + continue; + } + if (((getStrategy((audio_stream_type_t)i) == strategy) || + (NUM_STRATEGIES == strategy)) && + outputDesc->isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage) +{ + return mEngine->getForceUse(usage); +} + +bool AudioPolicyManager::isInCall() +{ + return isStateInCall(mEngine->getPhoneState()); +} + +bool AudioPolicyManager::isStateInCall(int state) +{ + return is_state_in_call(state); +} + }; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h index cbdafa6..02b678a 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#pragma once #include <stdint.h> #include <sys/types.h> @@ -26,6 +27,21 @@ #include <media/AudioPolicy.h> #include "AudioPolicyInterface.h" +#include <AudioPolicyManagerInterface.h> +#include <AudioPolicyManagerObserver.h> +#include <AudioGain.h> +#include <AudioPort.h> +#include <AudioPatch.h> +#include <ConfigParsingUtils.h> +#include <DeviceDescriptor.h> +#include <IOProfile.h> +#include <HwModule.h> +#include <AudioInputDescriptor.h> +#include <AudioOutputDescriptor.h> +#include <AudioPolicyMix.h> +#include <EffectDescriptor.h> +#include <SoundTriggerSession.h> +#include <StreamDescriptor.h> namespace android { @@ -38,9 +54,7 @@ namespace android { // Time in milliseconds during which we consider that music is still active after a music // track was stopped - see computeVolume() #define SONIFICATION_HEADSET_MUSIC_DELAY 5000 -// Time in milliseconds after media stopped playing during which we consider that the -// sonification should be as unobtrusive as during the time media was playing. -#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 + // Time in milliseconds during witch some streams are muted while the audio path // is switched #define MUTE_TIME_MS 2000 @@ -53,14 +67,12 @@ namespace android { // Can be overridden by the audio.offload.min.duration.secs property #define OFFLOAD_DEFAULT_MIN_DURATION_SECS 60 -#define MAX_MIXER_SAMPLING_RATE 48000 -#define MAX_MIXER_CHANNEL_COUNT 8 - // ---------------------------------------------------------------------------- // AudioPolicyManager implements audio policy manager behavior common to all platforms. // ---------------------------------------------------------------------------- -class AudioPolicyManager: public AudioPolicyInterface +class AudioPolicyManager : public AudioPolicyInterface, public AudioPolicyManagerObserver + #ifdef AUDIO_POLICY_TEST , public Thread #endif //AUDIO_POLICY_TEST @@ -73,13 +85,15 @@ public: // AudioPolicyInterface virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); + const char *device_address, + const char *device_name); virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); virtual void setPhoneState(audio_mode_t state); virtual void setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); + virtual void setSystemProperty(const char* property, const char* value); virtual status_t initCheck(); virtual audio_io_handle_t getOutput(audio_stream_type_t stream, @@ -149,16 +163,28 @@ public: uint32_t strategy, int session, int id); - virtual status_t unregisterEffect(int id); - virtual status_t setEffectEnabled(int id, bool enabled); + virtual status_t unregisterEffect(int id) + { + return mEffects.unregisterEffect(id); + } + virtual status_t setEffectEnabled(int id, bool enabled) + { + return mEffects.setEffectEnabled(id, enabled); + } - virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const + { + return mOutputs.isStreamActive(stream, inPastMs); + } // return whether a stream is playing remotely, override to change the definition of // local/remote playback, used for instance by notification manager to not make // media players lose audio focus when not playing locally // For the base implementation, "remotely" means playing during screen mirroring which // uses an output for playback with a non-empty, non "0" address. - virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const + { + return mOutputs.isStreamActiveRemotely(stream, inPastMs); + } virtual bool isSourceActive(audio_source_t source) const; virtual status_t dump(int fd); @@ -186,402 +212,63 @@ public: audio_io_handle_t *ioHandle, audio_devices_t *device); - virtual status_t releaseSoundTriggerSession(audio_session_t session); + virtual status_t releaseSoundTriggerSession(audio_session_t session) + { + return mSoundTriggerSessions.releaseSession(session); + } virtual status_t registerPolicyMixes(Vector<AudioMix> mixes); virtual status_t unregisterPolicyMixes(Vector<AudioMix> mixes); -protected: - - enum routing_strategy { - STRATEGY_MEDIA, - STRATEGY_PHONE, - STRATEGY_SONIFICATION, - STRATEGY_SONIFICATION_RESPECTFUL, - STRATEGY_DTMF, - STRATEGY_ENFORCED_AUDIBLE, - STRATEGY_TRANSMITTED_THROUGH_SPEAKER, - STRATEGY_ACCESSIBILITY, - STRATEGY_REROUTING, - NUM_STRATEGIES - }; - - // 4 points to define the volume attenuation curve, each characterized by the volume - // index (from 0 to 100) at which they apply, and the attenuation in dB at that index. - // we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl() - - enum { VOLMIN = 0, VOLKNEE1 = 1, VOLKNEE2 = 2, VOLMAX = 3, VOLCNT = 4}; - - class VolumeCurvePoint - { - public: - int mIndex; - float mDBAttenuation; - }; - - // device categories used for volume curve management. - enum device_category { - DEVICE_CATEGORY_HEADSET, - DEVICE_CATEGORY_SPEAKER, - DEVICE_CATEGORY_EARPIECE, - DEVICE_CATEGORY_EXT_MEDIA, - DEVICE_CATEGORY_CNT - }; - - class HwModule; - - class AudioGain: public RefBase - { - public: - AudioGain(int index, bool useInChannelMask); - virtual ~AudioGain() {} - - void dump(int fd, int spaces, int index) const; - - void getDefaultConfig(struct audio_gain_config *config); - status_t checkConfig(const struct audio_gain_config *config); - int mIndex; - struct audio_gain mGain; - bool mUseInChannelMask; - }; - - class AudioPort: public virtual RefBase - { - public: - AudioPort(const String8& name, audio_port_type_t type, - audio_port_role_t role, const sp<HwModule>& module); - virtual ~AudioPort() {} - - virtual void toAudioPort(struct audio_port *port) const; - - void importAudioPort(const sp<AudioPort> port); - void clearCapabilities(); - - void loadSamplingRates(char *name); - void loadFormats(char *name); - void loadOutChannels(char *name); - void loadInChannels(char *name); - - audio_gain_mode_t loadGainMode(char *name); - void loadGain(cnode *root, int index); - virtual void loadGains(cnode *root); - - // searches for an exact match - status_t checkExactSamplingRate(uint32_t samplingRate) const; - // searches for a compatible match, and returns the best match via updatedSamplingRate - status_t checkCompatibleSamplingRate(uint32_t samplingRate, - uint32_t *updatedSamplingRate) const; - // searches for an exact match - status_t checkExactChannelMask(audio_channel_mask_t channelMask) const; - // searches for a compatible match, currently implemented for input channel masks only - status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const; - status_t checkFormat(audio_format_t format) const; - status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; - - uint32_t pickSamplingRate() const; - audio_channel_mask_t pickChannelMask() const; - audio_format_t pickFormat() const; - - static const audio_format_t sPcmFormatCompareTable[]; - static int compareFormats(audio_format_t format1, audio_format_t format2); - - void dump(int fd, int spaces) const; - - String8 mName; - audio_port_type_t mType; - audio_port_role_t mRole; - bool mUseInChannelMask; - // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats - // indicates the supported parameters should be read from the output stream - // after it is opened for the first time - Vector <uint32_t> mSamplingRates; // supported sampling rates - Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks - Vector <audio_format_t> mFormats; // supported audio formats - Vector < sp<AudioGain> > mGains; // gain controllers - sp<HwModule> mModule; // audio HW module exposing this I/O stream - uint32_t mFlags; // attribute flags (e.g primary output, - // direct output...). - }; - - class AudioPortConfig: public virtual RefBase - { - public: - AudioPortConfig(); - virtual ~AudioPortConfig() {} - - status_t applyAudioPortConfig(const struct audio_port_config *config, - struct audio_port_config *backupConfig = NULL); - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const = 0; - virtual sp<AudioPort> getAudioPort() const = 0; - uint32_t mSamplingRate; - audio_format_t mFormat; - audio_channel_mask_t mChannelMask; - struct audio_gain_config mGain; - }; + // Audio policy configuration file parsing (audio_policy.conf) + // TODO candidates to be moved to ConfigParsingUtils + void defaultAudioPolicyConfig(void); + // return the strategy corresponding to a given stream type + routing_strategy getStrategy(audio_stream_type_t stream) const; - class AudioPatch: public RefBase + // From AudioPolicyManagerObserver + virtual const AudioPatchCollection &getAudioPatches() const { - public: - AudioPatch(audio_patch_handle_t handle, - const struct audio_patch *patch, uid_t uid) : - mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {} - - status_t dump(int fd, int spaces, int index) const; - - audio_patch_handle_t mHandle; - struct audio_patch mPatch; - uid_t mUid; - audio_patch_handle_t mAfPatchHandle; - }; - - class DeviceDescriptor: public AudioPort, public AudioPortConfig + return mAudioPatches; + } + virtual const SoundTriggerSessionCollection &getSoundTriggerSessionCollection() const { - public: - DeviceDescriptor(const String8& name, audio_devices_t type); - - virtual ~DeviceDescriptor() {} - - bool equals(const sp<DeviceDescriptor>& other) const; - - // AudioPortConfig - virtual sp<AudioPort> getAudioPort() const { return (AudioPort*) this; } - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - - // AudioPort - virtual void loadGains(cnode *root); - virtual void toAudioPort(struct audio_port *port) const; - - status_t dump(int fd, int spaces, int index) const; - - audio_devices_t mDeviceType; - String8 mAddress; - audio_port_handle_t mId; - }; - - class DeviceVector : public SortedVector< sp<DeviceDescriptor> > + return mSoundTriggerSessions; + } + virtual const AudioPolicyMixCollection &getAudioPolicyMixCollection() const { - public: - DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {} - - ssize_t add(const sp<DeviceDescriptor>& item); - ssize_t remove(const sp<DeviceDescriptor>& item); - ssize_t indexOf(const sp<DeviceDescriptor>& item) const; - - audio_devices_t types() const { return mDeviceTypes; } - - void loadDevicesFromType(audio_devices_t types); - void loadDevicesFromName(char *name, const DeviceVector& declaredDevices); - - sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const; - DeviceVector getDevicesFromType(audio_devices_t types) const; - sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const; - sp<DeviceDescriptor> getDeviceFromName(const String8& name) const; - DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) - const; - - private: - void refreshTypes(); - audio_devices_t mDeviceTypes; - }; - - // the IOProfile class describes the capabilities of an output or input stream. - // It is currently assumed that all combination of listed parameters are supported. - // It is used by the policy manager to determine if an output or input is suitable for - // a given use case, open/close it accordingly and connect/disconnect audio tracks - // to/from it. - class IOProfile : public AudioPort + return mPolicyMixes; + } + virtual const AudioOutputCollection &getOutputs() const { - public: - IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); - virtual ~IOProfile(); - - // This method is used for both output and input. - // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. - // For input, flags is interpreted as audio_input_flags_t. - // TODO: merge audio_output_flags_t and audio_input_flags_t. - bool isCompatibleProfile(audio_devices_t device, - String8 address, - uint32_t samplingRate, - uint32_t *updatedSamplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - uint32_t flags) const; - - void dump(int fd); - void log(); - - DeviceVector mSupportedDevices; // supported devices - // (devices this output can be routed to) - }; - - class HwModule : public RefBase + return mOutputs; + } + virtual const AudioInputCollection &getInputs() const { - public: - HwModule(const char *name); - ~HwModule(); - - status_t loadOutput(cnode *root); - status_t loadInput(cnode *root); - status_t loadDevice(cnode *root); - - status_t addOutputProfile(String8 name, const audio_config_t *config, - audio_devices_t device, String8 address); - status_t removeOutputProfile(String8 name); - status_t addInputProfile(String8 name, const audio_config_t *config, - audio_devices_t device, String8 address); - status_t removeInputProfile(String8 name); - - void dump(int fd); - - const char *const mName; // base name of the audio HW module (primary, a2dp ...) - uint32_t mHalVersion; // audio HAL API version - audio_module_handle_t mHandle; - Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module - Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module - DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf - - }; - - // default volume curve - static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManager::VOLCNT]; - // default volume curve for media strategy - static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; - // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) - static const VolumeCurvePoint sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT]; - // volume curve for media strategy on speakers - static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT]; - // volume curve for sonification strategy on speakers - static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sLinearVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sSilentVolumeCurve[AudioPolicyManager::VOLCNT]; - static const VolumeCurvePoint sFullScaleVolumeCurve[AudioPolicyManager::VOLCNT]; - // default volume curves per stream and device category. See initializeVolumeCurves() - static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][DEVICE_CATEGORY_CNT]; - - // descriptor for audio outputs. Used to maintain current configuration of each opened audio output - // and keep track of the usage of this output by each audio stream type. - class AudioOutputDescriptor: public AudioPortConfig + return mInputs; + } + virtual const DeviceVector &getAvailableOutputDevices() const { - public: - AudioOutputDescriptor(const sp<IOProfile>& profile); - - status_t dump(int fd); - - audio_devices_t device() const; - void changeRefCount(audio_stream_type_t stream, int delta); - - bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } - audio_devices_t supportedDevices(); - uint32_t latency(); - bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc); - bool isActive(uint32_t inPastMs = 0) const; - bool isStreamActive(audio_stream_type_t stream, - uint32_t inPastMs = 0, - nsecs_t sysTime = 0) const; - bool isStrategyActive(routing_strategy strategy, - uint32_t inPastMs = 0, - nsecs_t sysTime = 0) const; - - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - virtual sp<AudioPort> getAudioPort() const { return mProfile; } - void toAudioPort(struct audio_port *port) const; - - audio_port_handle_t mId; - audio_io_handle_t mIoHandle; // output handle - uint32_t mLatency; // - audio_output_flags_t mFlags; // - audio_devices_t mDevice; // current device this output is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy - audio_patch_handle_t mPatchHandle; - uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output - nsecs_t mStopTime[AUDIO_STREAM_CNT]; - sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output - sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output - float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume - int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter - const sp<IOProfile> mProfile; // I/O profile this output derives from - bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible - // device selection. See checkDeviceMuteStrategies() - uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) - }; - - // descriptor for audio inputs. Used to maintain current configuration of each opened audio input - // and keep track of the usage of this input. - class AudioInputDescriptor: public AudioPortConfig + return mAvailableOutputDevices; + } + virtual const DeviceVector &getAvailableInputDevices() const { - public: - AudioInputDescriptor(const sp<IOProfile>& profile); - - status_t dump(int fd); - - audio_port_handle_t mId; - audio_io_handle_t mIoHandle; // input handle - audio_devices_t mDevice; // current device this input is routed to - AudioMix *mPolicyMix; // non NULL when used by a dynamic policy - audio_patch_handle_t mPatchHandle; - uint32_t mRefCount; // number of AudioRecord clients using - // this input - uint32_t mOpenRefCount; - audio_source_t mInputSource; // input source selected by application - //(mediarecorder.h) - const sp<IOProfile> mProfile; // I/O profile this output derives from - SortedVector<audio_session_t> mSessions; // audio sessions attached to this input - bool mIsSoundTrigger; // used by a soundtrigger capture - - virtual void toAudioPortConfig(struct audio_port_config *dstConfig, - const struct audio_port_config *srcConfig = NULL) const; - virtual sp<AudioPort> getAudioPort() const { return mProfile; } - void toAudioPort(struct audio_port *port) const; - }; - - // stream descriptor used for volume control - class StreamDescriptor + return mAvailableInputDevices; + } + virtual StreamDescriptorCollection &getStreamDescriptors() { - public: - StreamDescriptor(); - - int getVolumeIndex(audio_devices_t device); - void dump(int fd); - - int mIndexMin; // min volume index - int mIndexMax; // max volume index - KeyedVector<audio_devices_t, int> mIndexCur; // current volume index per device - bool mCanBeMuted; // true is the stream can be muted - - const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; - }; - - // stream descriptor used for volume control - class EffectDescriptor : public RefBase + return mStreams; + } + virtual const sp<DeviceDescriptor> &getDefaultOutputDevice() const { - public: - - status_t dump(int fd); - - int mIo; // io the effect is attached to - routing_strategy mStrategy; // routing strategy the effect is associated to - int mSession; // audio session the effect is on - effect_descriptor_t mDesc; // effect descriptor - bool mEnabled; // enabled state: CPU load being used or not - }; - + return mDefaultOutputDevice; + } +protected: void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc); + void removeOutput(audio_io_handle_t output); void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc); - // return the strategy corresponding to a given stream type - static routing_strategy getStrategy(audio_stream_type_t stream); - // return appropriate device for streams handled by the specified strategy according to current // phone state, connected devices... // if fromCache is true, the device is returned from mDeviceForStrategy[], @@ -596,6 +283,9 @@ protected: virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, bool fromCache); + bool isStrategyActive(const sp<AudioOutputDescriptor> outputDesc, routing_strategy strategy, + uint32_t inPastMs = 0, nsecs_t sysTime = 0) const; + // change the route of the specified output. Returns the number of ms we have slept to // allow new routing to take effect in certain cases. virtual uint32_t setOutputDevice(audio_io_handle_t output, @@ -617,16 +307,6 @@ protected: // select input device corresponding to requested audio source virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource); - // return io handle of active input or 0 if no input is active - // Only considers inputs from physical devices (e.g. main mic, headset mic) when - // ignoreVirtualInputs is true. - audio_io_handle_t getActiveInput(bool ignoreVirtualInputs = true); - - uint32_t activeInputsCount() const; - - // initialize volume curves for each strategy and device category - void initializeVolumeCurves(); - // compute the actual volume for a given stream according to the requested index and a particular // device virtual float computeVolume(audio_stream_type_t stream, int index, @@ -659,9 +339,10 @@ protected: // a special tone in the device used for communication void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); + audio_mode_t getPhoneState(); + // true if device is in a telephony or VoIP call virtual bool isInCall(); - // true if given state represents a device in a telephony or VoIP call virtual bool isStateInCall(int state); @@ -699,9 +380,6 @@ protected: // manages A2DP output suspend/restore according to phone state and BT SCO usage void checkA2dpSuspend(); - // returns the A2DP output handle if it is open or 0 otherwise - audio_io_handle_t getA2dpOutput(); - // selects the most appropriate device on output for current state // must be called every time a condition that affects the device choice for a given output is // changed: connected device, phone state, force use, output start, output stop.. @@ -718,24 +396,23 @@ protected: // selects the most appropriate device on input for current state audio_devices_t getNewInputDevice(audio_io_handle_t input); - virtual uint32_t getMaxEffectsCpuLoad(); - virtual uint32_t getMaxEffectsMemory(); + virtual uint32_t getMaxEffectsCpuLoad() + { + return mEffects.getMaxEffectsCpuLoad(); + } + + virtual uint32_t getMaxEffectsMemory() + { + return mEffects.getMaxEffectsMemory(); + } #ifdef AUDIO_POLICY_TEST virtual bool threadLoop(); void exit(); int testOutputIndex(audio_io_handle_t output); #endif //AUDIO_POLICY_TEST - status_t setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled); - - // returns the category the device belongs to with regard to volume curve management - static device_category getDeviceCategory(audio_devices_t device); - - // extract one device relevant for volume control from multiple device selection - static audio_devices_t getDeviceForVolume(audio_devices_t device); - SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device, - DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs); + AudioOutputCollection openOutputs); bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, SortedVector<audio_io_handle_t>& outputs2); @@ -765,79 +442,57 @@ protected: audio_io_handle_t selectOutputForEffects(const SortedVector<audio_io_handle_t>& outputs); - bool isNonOffloadableEffectEnabled(); - - virtual status_t addAudioPatch(audio_patch_handle_t handle, - const sp<AudioPatch>& patch); - virtual status_t removeAudioPatch(audio_patch_handle_t handle); + virtual status_t addAudioPatch(audio_patch_handle_t handle, const sp<AudioPatch>& patch) + { + return mAudioPatches.addAudioPatch(handle, patch); + } + virtual status_t removeAudioPatch(audio_patch_handle_t handle) + { + return mAudioPatches.removeAudioPatch(handle); + } - sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const; - sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const; - sp<HwModule> getModuleForDevice(audio_devices_t device) const; - sp<HwModule> getModuleFromName(const char *name) const; - audio_devices_t availablePrimaryOutputDevices(); - audio_devices_t availablePrimaryInputDevices(); + audio_devices_t availablePrimaryOutputDevices() const + { + return mOutputs.getSupportedDevices(mPrimaryOutput) & mAvailableOutputDevices.types(); + } + audio_devices_t availablePrimaryInputDevices() const + { + return mAvailableInputDevices.getDevicesFromHwModule( + mOutputs.valueFor(mPrimaryOutput)->getModuleHandle()); + } void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0); - // - // Audio policy configuration file parsing (audio_policy.conf) - // - static uint32_t stringToEnum(const struct StringToEnum *table, - size_t size, - const char *name); - static const char *enumToString(const struct StringToEnum *table, - size_t size, - uint32_t value); - static bool stringToBool(const char *value); - static uint32_t parseOutputFlagNames(char *name); - static uint32_t parseInputFlagNames(char *name); - static audio_devices_t parseDeviceNames(char *name); - void loadHwModule(cnode *root); - void loadHwModules(cnode *root); - void loadGlobalConfig(cnode *root, const sp<HwModule>& module); - status_t loadAudioPolicyConfig(const char *path); - void defaultAudioPolicyConfig(void); - - uid_t mUidCached; AudioPolicyClientInterface *mpClientInterface; // audio policy client interface audio_io_handle_t mPrimaryOutput; // primary output handle // list of descriptors for outputs currently opened - DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mOutputs; + AudioOutputCollection mOutputs; // copy of mOutputs before setDeviceConnectionState() opens new outputs // reset to mOutputs when updateDevicesAndOutputs() is called. - DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mPreviousOutputs; - DefaultKeyedVector<audio_io_handle_t, sp<AudioInputDescriptor> > mInputs; // list of input descriptors + AudioOutputCollection mPreviousOutputs; + AudioInputCollection mInputs; // list of input descriptors DeviceVector mAvailableOutputDevices; // all available output devices DeviceVector mAvailableInputDevices; // all available input devices - int mPhoneState; // current phone state - audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; // current forced use configuration - StreamDescriptor mStreams[AUDIO_STREAM_CNT]; // stream descriptors for volume control - bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected + StreamDescriptorCollection mStreams; // stream descriptors for volume control + bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; - float mLastVoiceVolume; // last voice volume value sent to audio HAL - - // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units - static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000; - // Maximum memory allocated to audio effects in KB - static const uint32_t MAX_EFFECTS_MEMORY = 512; - uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects - uint32_t mTotalEffectsMemory; // current memory used by effects - KeyedVector<int, sp<EffectDescriptor> > mEffects; // list of registered audio effects + float mLastVoiceVolume; // last voice volume value sent to audio HAL + + EffectDescriptorCollection mEffects; // list of registered audio effects bool mA2dpSuspended; // true if A2DP output is suspended sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path // to boost soft sounds, used to adjust volume curves accordingly - Vector < sp<HwModule> > mHwModules; - volatile int32_t mNextUniqueId; + HwModuleCollection mHwModules; + volatile int32_t mAudioPortGeneration; - DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches; + AudioPatchCollection mAudioPatches; - DefaultKeyedVector<audio_session_t, audio_io_handle_t> mSoundTriggerSessions; + SoundTriggerSessionCollection mSoundTriggerSessions; sp<AudioPatch> mCallTxPatch; sp<AudioPatch> mCallRxPatch; @@ -854,16 +509,7 @@ protected: uint32_t mBeaconPlayingRefCount;// ref count for the playing beacon streams bool mBeaconMuted; // has STREAM_TTS been muted - // custom mix entry in mPolicyMixes - class AudioPolicyMix : public RefBase { - public: - AudioPolicyMix() {} - - AudioMix mMix; // Audio policy mix descriptor - sp<AudioOutputDescriptor> mOutput; // Corresponding output stream - }; - DefaultKeyedVector<String8, sp<AudioPolicyMix> > mPolicyMixes; // list of registered mixes - + AudioPolicyMixCollection mPolicyMixes; // list of registered mixes #ifdef AUDIO_POLICY_TEST Mutex mLock; @@ -879,16 +525,15 @@ protected: uint32_t mTestChannels; uint32_t mTestLatencyMs; #endif //AUDIO_POLICY_TEST - static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, - int indexInUi); - static bool isVirtualInputDevice(audio_devices_t device); - uint32_t nextUniqueId(); + uint32_t nextAudioPortGeneration(); + + // Audio Policy Engine Interface. + AudioPolicyManagerInterface *mEngine; private: // updates device caching and output for streams that can influence the // routing of notifications void handleNotificationRoutingForStream(audio_stream_type_t stream); - static bool deviceDistinguishesOnAddress(audio_devices_t device); // find the outputs on a given output descriptor that have the given address. // to be called on an AudioOutputDescriptor whose supported devices (as defined // in mProfile->mSupportedDevices) matches the device whose address is to be matched. @@ -911,8 +556,6 @@ private: const audio_offload_info_t *offloadInfo); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); - // return true if any output is playing anything besides the stream to ignore - bool isAnyOutputActive(audio_stream_type_t streamToIgnore); // event is one of STARTING_OUTPUT, STARTING_BEACON, STOPPING_OUTPUT, STOPPING_BEACON // returns 0 if no mute/unmute event happened, the largest latency of the device where // the mute/unmute happened @@ -928,10 +571,8 @@ private: // Called by setDeviceConnectionState(). status_t setDeviceConnectionStateInt(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); - sp<DeviceDescriptor> getDeviceDescriptor(const audio_devices_t device, - const char *device_address); - + const char *device_address, + const char *device_name); }; }; diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp index 3e090e9..3e090e9 100644 --- a/services/audiopolicy/AudioPolicyClientImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp diff --git a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp index a79f8ae..a79f8ae 100644 --- a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyClientImplLegacy.cpp diff --git a/services/audiopolicy/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp index e6ace20..e6ace20 100644 --- a/services/audiopolicy/AudioPolicyEffects.cpp +++ b/services/audiopolicy/service/AudioPolicyEffects.cpp diff --git a/services/audiopolicy/AudioPolicyEffects.h b/services/audiopolicy/service/AudioPolicyEffects.h index 3dec437..3dec437 100644 --- a/services/audiopolicy/AudioPolicyEffects.h +++ b/services/audiopolicy/service/AudioPolicyEffects.h diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp index a45dbb3..e9ff838 100644 --- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp @@ -28,7 +28,8 @@ namespace android { status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name) { if (mAudioPolicyManager == NULL) { return NO_INIT; @@ -46,8 +47,8 @@ status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, ALOGV("setDeviceConnectionState()"); Mutex::Autolock _l(mLock); - return mAudioPolicyManager->setDeviceConnectionState(device, - state, device_address); + return mAudioPolicyManager->setDeviceConnectionState(device, state, + device_address, device_name); } audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp index b8846c6..5a91192 100644 --- a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp +++ b/services/audiopolicy/service/AudioPolicyInterfaceImplLegacy.cpp @@ -33,7 +33,8 @@ namespace android { status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address) + const char *device_address, + const char *device_name __unused) { if (mpAudioPolicy == NULL) { return NO_INIT; diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp index eb9116d..00f188f 100644 --- a/services/audiopolicy/AudioPolicyService.cpp +++ b/services/audiopolicy/service/AudioPolicyService.cpp @@ -532,7 +532,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() mLock.unlock(); svc.clear(); mLock.lock(); - if (!exitPending() && mAudioCommands.isEmpty()) { + if (!exitPending() && (mAudioCommands.isEmpty() || waitTime != INT64_MAX)) { // release delayed commands wake lock release_wake_lock(mName.string()); ALOGV("AudioCommandThread() going to sleep"); diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h index 80284a4..0378384 100644 --- a/services/audiopolicy/AudioPolicyService.h +++ b/services/audiopolicy/service/AudioPolicyService.h @@ -35,7 +35,7 @@ #include <hardware_legacy/AudioPolicyInterface.h> #endif #include "AudioPolicyEffects.h" -#include "AudioPolicyManager.h" +#include "managerdefault/AudioPolicyManager.h" namespace android { @@ -61,7 +61,8 @@ public: virtual status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address); + const char *device_address, + const char *device_name); virtual audio_policy_dev_state_t getDeviceConnectionState( audio_devices_t device, const char *device_address); diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index e184d97..9c60911 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -23,8 +23,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ CameraService.cpp \ CameraDeviceFactory.cpp \ + CameraFlashlight.cpp \ common/Camera2ClientBase.cpp \ common/CameraDeviceBase.cpp \ + common/CameraModule.cpp \ common/FrameProcessorBase.cpp \ api1/CameraClient.cpp \ api1/Camera2Client.cpp \ @@ -40,7 +42,6 @@ LOCAL_SRC_FILES:= \ api1/client2/CaptureSequencer.cpp \ api1/client2/ZslProcessor3.cpp \ api2/CameraDeviceClient.cpp \ - api_pro/ProCamera2Client.cpp \ device2/Camera2Device.cpp \ device3/Camera3Device.cpp \ device3/Camera3Stream.cpp \ @@ -52,6 +53,7 @@ LOCAL_SRC_FILES:= \ device3/StatusTracker.cpp \ gui/RingBufferConsumer.cpp \ utils/CameraTraces.cpp \ + utils/AutoConditionLock.cpp \ LOCAL_SHARED_LIBRARIES:= \ libui \ diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index bfef50e..6589e27 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -48,6 +48,7 @@ sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) { case CAMERA_DEVICE_API_VERSION_3_0: case CAMERA_DEVICE_API_VERSION_3_1: case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: device = new Camera3Device(cameraId); break; default: diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp new file mode 100644 index 0000000..8613ac6 --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.cpp @@ -0,0 +1,886 @@ +/* + * Copyright (C) 2015 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 "CameraFlashlight" +#define ATRACE_TAG ATRACE_TAG_CAMERA +// #define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <cutils/properties.h> + +#include "camera/CameraMetadata.h" +#include "CameraFlashlight.h" +#include "gui/IGraphicBufferConsumer.h" +#include "gui/BufferQueue.h" +#include "camera/camera2/CaptureRequest.h" +#include "CameraDeviceFactory.h" + + +namespace android { + +///////////////////////////////////////////////////////////////////// +// CameraFlashlight implementation begins +// used by camera service to control flashflight. +///////////////////////////////////////////////////////////////////// +CameraFlashlight::CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mFlashlightMapInitialized(false) { +} + +CameraFlashlight::~CameraFlashlight() { +} + +status_t CameraFlashlight::createFlashlightControl(const String8& cameraId) { + ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__, + cameraId.string()); + if (mFlashControl != NULL) { + return INVALID_OPERATION; + } + + status_t res = OK; + + if (mCameraModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) { + mFlashControl = new ModuleFlashControl(*mCameraModule, *mCallbacks); + if (mFlashControl == NULL) { + ALOGV("%s: cannot create flash control for module api v2.4+", + __FUNCTION__); + return NO_MEMORY; + } + } else { + uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + + if (mCameraModule->getModuleApiVersion() >= + CAMERA_MODULE_API_VERSION_2_0) { + camera_info info; + res = mCameraModule->getCameraInfo( + atoi(String8(cameraId).string()), &info); + if (res) { + ALOGE("%s: failed to get camera info for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + deviceVersion = info.device_version; + } + + if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { + CameraDeviceClientFlashControl *flashControl = + new CameraDeviceClientFlashControl(*mCameraModule, + *mCallbacks); + if (!flashControl) { + return NO_MEMORY; + } + + mFlashControl = flashControl; + } else { + mFlashControl = + new CameraHardwareInterfaceFlashControl(*mCameraModule, + *mCallbacks); + } + } + + return OK; +} + +status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) { + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + ALOGV("%s: set torch mode of camera %s to %d", __FUNCTION__, + cameraId.string(), enabled); + + status_t res = OK; + Mutex::Autolock l(mLock); + + if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) { + // This case is needed to avoid state corruption during the following call sequence: + // CameraService::setTorchMode for camera ID 0 begins, does torch status checks + // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends + // CameraService::setTorchMode for camera ID 0 continues, calls + // CameraFlashlight::setTorchMode + + // TODO: Move torch status checks and state updates behind this CameraFlashlight lock + // to avoid other similar race conditions. + ALOGE("%s: Camera device %s is in use, cannot set torch mode.", + __FUNCTION__, cameraId.string()); + return -EBUSY; + } + + if (mFlashControl == NULL) { + if (enabled == false) { + return OK; + } + + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + return res; + } + + // if flash control already exists, turning on torch mode may fail if it's + // tied to another camera device for module v2.3 and below. + res = mFlashControl->setTorchMode(cameraId, enabled); + if (res == BAD_INDEX) { + // flash control is tied to another camera device, need to close it and + // try again. + mFlashControl.clear(); + res = createFlashlightControl(cameraId); + if (res) { + return res; + } + res = mFlashControl->setTorchMode(cameraId, enabled); + } + + return res; +} + +status_t CameraFlashlight::findFlashUnits() { + Mutex::Autolock l(mLock); + status_t res; + int32_t numCameras = mCameraModule->getNumberOfCameras(); + + mHasFlashlightMap.clear(); + mFlashlightMapInitialized = false; + + for (int32_t i = 0; i < numCameras; i++) { + bool hasFlash = false; + String8 id = String8::format("%d", i); + + res = createFlashlightControl(id); + if (res) { + ALOGE("%s: failed to create flash control for %s", __FUNCTION__, + id.string()); + } else { + res = mFlashControl->hasFlashUnit(id, &hasFlash); + if (res == -EUSERS || res == -EBUSY) { + ALOGE("%s: failed to check if camera %s has a flash unit. Some " + "camera devices may be opened", __FUNCTION__, + id.string()); + return res; + } else if (res) { + ALOGE("%s: failed to check if camera %s has a flash unit. %s" + " (%d)", __FUNCTION__, id.string(), strerror(-res), + res); + } + + mFlashControl.clear(); + } + mHasFlashlightMap.add(id, hasFlash); + } + + mFlashlightMapInitialized = true; + return OK; +} + +bool CameraFlashlight::hasFlashUnit(const String8& cameraId) { + status_t res; + + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId); +} + +bool CameraFlashlight::hasFlashUnitLocked(const String8& cameraId) { + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return false; + } + + ssize_t index = mHasFlashlightMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + ALOGE("%s: camera %s not present when findFlashUnits() was called", + __FUNCTION__, cameraId.string()); + return false; + } + + return mHasFlashlightMap.valueAt(index); +} + +status_t CameraFlashlight::prepareDeviceOpen(const String8& cameraId) { + ALOGV("%s: prepare for device open", __FUNCTION__); + + Mutex::Autolock l(mLock); + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + if (mCameraModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_4) { + // framework is going to open a camera device, all flash light control + // should be closed for backward compatible support. + mFlashControl.clear(); + + if (mOpenedCameraIds.size() == 0) { + // notify torch unavailable for all cameras with a flash + int numCameras = mCameraModule->getNumberOfCameras(); + for (int i = 0; i < numCameras; i++) { + if (hasFlashUnitLocked(String8::format("%d", i))) { + mCallbacks->torch_mode_status_change(mCallbacks, + String8::format("%d", i).string(), + TORCH_MODE_STATUS_NOT_AVAILABLE); + } + } + } + + // close flash control that may be opened by calling hasFlashUnitLocked. + mFlashControl.clear(); + } + + if (mOpenedCameraIds.indexOf(cameraId) == NAME_NOT_FOUND) { + mOpenedCameraIds.add(cameraId); + } + + return OK; +} + +status_t CameraFlashlight::deviceClosed(const String8& cameraId) { + ALOGV("%s: device %s is closed", __FUNCTION__, cameraId.string()); + + Mutex::Autolock l(mLock); + if (!mFlashlightMapInitialized) { + ALOGE("%s: findFlashUnits() must be called before this method."); + return NO_INIT; + } + + ssize_t index = mOpenedCameraIds.indexOf(cameraId); + if (index == NAME_NOT_FOUND) { + ALOGE("%s: couldn't find camera %s in the opened list", __FUNCTION__, + cameraId.string()); + } else { + mOpenedCameraIds.removeAt(index); + } + + // Cannot do anything until all cameras are closed. + if (mOpenedCameraIds.size() != 0) + return OK; + + if (mCameraModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_4) { + // notify torch available for all cameras with a flash + int numCameras = mCameraModule->getNumberOfCameras(); + for (int i = 0; i < numCameras; i++) { + if (hasFlashUnitLocked(String8::format("%d", i))) { + mCallbacks->torch_mode_status_change(mCallbacks, + String8::format("%d", i).string(), + TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } + } + + return OK; +} +// CameraFlashlight implementation ends + + +FlashControlBase::~FlashControlBase() { +} + +///////////////////////////////////////////////////////////////////// +// ModuleFlashControl implementation begins +// Flash control for camera module v2.4 and above. +///////////////////////////////////////////////////////////////////// +ModuleFlashControl::ModuleFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule) { +} + +ModuleFlashControl::~ModuleFlashControl() { +} + +status_t ModuleFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + *hasFlash = false; + Mutex::Autolock l(mLock); + + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), + &info); + if (res != 0) { + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t ModuleFlashControl::setTorchMode(const String8& cameraId, bool enabled) { + ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__, + cameraId.string(), enabled); + + Mutex::Autolock l(mLock); + return mCameraModule->setTorchMode(cameraId.string(), enabled); +} +// ModuleFlashControl implementation ends + +///////////////////////////////////////////////////////////////////// +// CameraDeviceClientFlashControl implementation begins +// Flash control for camera module <= v2.3 and camera HAL v2-v3 +///////////////////////////////////////////////////////////////////// +CameraDeviceClientFlashControl::CameraDeviceClientFlashControl( + CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mTorchEnabled(false), + mMetadata(NULL), + mStreaming(false) { +} + +CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() { + disconnectCameraDevice(); + if (mMetadata) { + delete mMetadata; + } + + mAnw.clear(); + mSurfaceTexture.clear(); + mProducer.clear(); + mConsumer.clear(); + + if (mTorchEnabled) { + if (mCallbacks) { + ALOGV("%s: notify the framework that torch was turned off", + __FUNCTION__); + mCallbacks->torch_mode_status_change(mCallbacks, + mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } +} + +status_t CameraDeviceClientFlashControl::initializeSurface( + sp<CameraDeviceBase> &device, int32_t width, int32_t height) { + status_t res; + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + + mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL, + true, true); + if (mSurfaceTexture == NULL) { + return NO_MEMORY; + } + + int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = mSurfaceTexture->setDefaultBufferSize(width, height); + if (res) { + return res; + } + res = mSurfaceTexture->setDefaultBufferFormat(format); + if (res) { + return res; + } + + mAnw = new Surface(mProducer, /*useAsync*/ true); + if (mAnw == NULL) { + return NO_MEMORY; + } + res = device->createStream(mAnw, width, height, format, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId); + if (res) { + return res; + } + + res = device->configureStreams(); + if (res) { + return res; + } + + return res; +} + +status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize( + const camera_info& info, int32_t *width, int32_t *height) { + if (!width || !height) { + return BAD_VALUE; + } + + int32_t w = INT32_MAX; + int32_t h = 1; + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry streamConfigs = + metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + for (size_t i = 0; i < streamConfigs.count; i += 4) { + int32_t fmt = streamConfigs.data.i32[i]; + if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) { + int32_t ww = streamConfigs.data.i32[i + 1]; + int32_t hh = streamConfigs.data.i32[i + 2]; + + if (w * h > ww * hh) { + w = ww; + h = hh; + } + } + } + + // if stream configuration is not found, try available processed sizes. + if (streamConfigs.count == 0) { + camera_metadata_entry availableProcessedSizes = + metadata.find(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (size_t i = 0; i < availableProcessedSizes.count; i += 2) { + int32_t ww = availableProcessedSizes.data.i32[i]; + int32_t hh = availableProcessedSizes.data.i32[i + 1]; + if (w * h > ww * hh) { + w = ww; + h = hh; + } + } + } + + if (w == INT32_MAX) { + return NAME_NOT_FOUND; + } + + *width = w; + *height = h; + + return OK; +} + +status_t CameraDeviceClientFlashControl::connectCameraDevice( + const String8& cameraId) { + camera_info info; + status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + cameraId.string()); + return res; + } + + sp<CameraDeviceBase> device = + CameraDeviceFactory::createDevice(atoi(cameraId.string())); + if (device == NULL) { + return NO_MEMORY; + } + + res = device->initialize(mCameraModule); + if (res) { + return res; + } + + int32_t width, height; + res = getSmallestSurfaceSize(info, &width, &height); + if (res) { + return res; + } + res = initializeSurface(device, width, height); + if (res) { + return res; + } + + mCameraId = cameraId; + mStreaming = (info.device_version <= CAMERA_DEVICE_API_VERSION_3_1); + mDevice = device; + + return OK; +} + +status_t CameraDeviceClientFlashControl::disconnectCameraDevice() { + if (mDevice != NULL) { + mDevice->disconnect(); + mDevice.clear(); + } + + return OK; +} + + + +status_t CameraDeviceClientFlashControl::hasFlashUnit(const String8& cameraId, + bool *hasFlash) { + ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__, + cameraId.string()); + + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId, hasFlash); + +} + +status_t CameraDeviceClientFlashControl::hasFlashUnitLocked( + const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + camera_info info; + status_t res = mCameraModule->getCameraInfo( + atoi(cameraId.string()), &info); + if (res != 0) { + ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__, + cameraId.string()); + return res; + } + + CameraMetadata metadata; + metadata = info.static_camera_characteristics; + camera_metadata_entry flashAvailable = + metadata.find(ANDROID_FLASH_INFO_AVAILABLE); + if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) { + *hasFlash = true; + } + + return OK; +} + +status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() { + status_t res; + + if (mMetadata == NULL) { + mMetadata = new CameraMetadata(); + if (mMetadata == NULL) { + return NO_MEMORY; + } + res = mDevice->createDefaultRequest( + CAMERA3_TEMPLATE_PREVIEW, mMetadata); + if (res) { + return res; + } + } + + uint8_t torchOn = ANDROID_FLASH_MODE_TORCH; + mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1); + mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1); + + uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON; + mMetadata->update(ANDROID_CONTROL_AE_MODE, &aeMode, 1); + + int32_t requestId = 0; + mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1); + + if (mStreaming) { + res = mDevice->setStreamingRequest(*mMetadata); + } else { + res = mDevice->capture(*mMetadata); + } + return res; +} + + + + +status_t CameraDeviceClientFlashControl::setTorchMode( + const String8& cameraId, bool enabled) { + bool hasFlash = false; + + Mutex::Autolock l(mLock); + status_t res = hasFlashUnitLocked(cameraId, &hasFlash); + + // pre-check + if (enabled) { + // invalid camera? + if (res) { + return -EINVAL; + } + // no flash unit? + if (!hasFlash) { + return -ENOSYS; + } + // already opened for a different device? + if (mDevice != NULL && cameraId != mCameraId) { + return BAD_INDEX; + } + } else if (mDevice == NULL || cameraId != mCameraId) { + // disabling the torch mode of an un-opened or different device. + return OK; + } else { + // disabling the torch mode of currently opened device + disconnectCameraDevice(); + mTorchEnabled = false; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + return OK; + } + + if (mDevice == NULL) { + res = connectCameraDevice(cameraId); + if (res) { + return res; + } + } + + res = submitTorchEnabledRequest(); + if (res) { + return res; + } + + mTorchEnabled = true; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON); + return OK; +} +// CameraDeviceClientFlashControl implementation ends + + +///////////////////////////////////////////////////////////////////// +// CameraHardwareInterfaceFlashControl implementation begins +// Flash control for camera module <= v2.3 and camera HAL v1 +///////////////////////////////////////////////////////////////////// +CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl( + CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks) : + mCameraModule(&cameraModule), + mCallbacks(&callbacks), + mTorchEnabled(false) { + +} + +CameraHardwareInterfaceFlashControl::~CameraHardwareInterfaceFlashControl() { + disconnectCameraDevice(); + + mAnw.clear(); + mSurfaceTexture.clear(); + mProducer.clear(); + mConsumer.clear(); + + if (mTorchEnabled) { + if (mCallbacks) { + ALOGV("%s: notify the framework that torch was turned off", + __FUNCTION__); + mCallbacks->torch_mode_status_change(mCallbacks, + mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + } + } +} + +status_t CameraHardwareInterfaceFlashControl::setTorchMode( + const String8& cameraId, bool enabled) { + Mutex::Autolock l(mLock); + + // pre-check + status_t res; + if (enabled) { + bool hasFlash = false; + res = hasFlashUnitLocked(cameraId, &hasFlash); + // invalid camera? + if (res) { + // hasFlashUnitLocked() returns BAD_INDEX if mDevice is connected to + // another camera device. + return res == BAD_INDEX ? BAD_INDEX : -EINVAL; + } + // no flash unit? + if (!hasFlash) { + return -ENOSYS; + } + } else if (mDevice == NULL || cameraId != mCameraId) { + // disabling the torch mode of an un-opened or different device. + return OK; + } else { + // disabling the torch mode of currently opened device + disconnectCameraDevice(); + mTorchEnabled = false; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF); + return OK; + } + + res = startPreviewAndTorch(); + if (res) { + return res; + } + + mTorchEnabled = true; + mCallbacks->torch_mode_status_change(mCallbacks, + cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON); + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::hasFlashUnit( + const String8& cameraId, bool *hasFlash) { + Mutex::Autolock l(mLock); + return hasFlashUnitLocked(cameraId, hasFlash); +} + +status_t CameraHardwareInterfaceFlashControl::hasFlashUnitLocked( + const String8& cameraId, bool *hasFlash) { + if (!hasFlash) { + return BAD_VALUE; + } + + status_t res; + if (mDevice == NULL) { + res = connectCameraDevice(cameraId); + if (res) { + return res; + } + } + + if (cameraId != mCameraId) { + return BAD_INDEX; + } + + const char *flashMode = + mParameters.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES); + if (flashMode && strstr(flashMode, CameraParameters::FLASH_MODE_TORCH)) { + *hasFlash = true; + } else { + *hasFlash = false; + } + + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::startPreviewAndTorch() { + status_t res = OK; + res = mDevice->startPreview(); + if (res) { + ALOGE("%s: start preview failed. %s (%d)", __FUNCTION__, + strerror(-res), res); + return res; + } + + mParameters.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_TORCH); + + return mDevice->setParameters(mParameters); +} + +status_t CameraHardwareInterfaceFlashControl::getSmallestSurfaceSize( + int32_t *width, int32_t *height) { + if (!width || !height) { + return BAD_VALUE; + } + + int32_t w = INT32_MAX; + int32_t h = 1; + Vector<Size> sizes; + + mParameters.getSupportedPreviewSizes(sizes); + for (size_t i = 0; i < sizes.size(); i++) { + Size s = sizes[i]; + if (w * h > s.width * s.height) { + w = s.width; + h = s.height; + } + } + + if (w == INT32_MAX) { + return NAME_NOT_FOUND; + } + + *width = w; + *height = h; + + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::initializePreviewWindow( + sp<CameraHardwareInterface> device, int32_t width, int32_t height) { + status_t res; + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + + mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL, + true, true); + if (mSurfaceTexture == NULL) { + return NO_MEMORY; + } + + int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = mSurfaceTexture->setDefaultBufferSize(width, height); + if (res) { + return res; + } + res = mSurfaceTexture->setDefaultBufferFormat(format); + if (res) { + return res; + } + + mAnw = new Surface(mProducer, /*useAsync*/ true); + if (mAnw == NULL) { + return NO_MEMORY; + } + + res = native_window_api_connect(mAnw.get(), NATIVE_WINDOW_API_CAMERA); + if (res) { + ALOGE("%s: Unable to connect to native window", __FUNCTION__); + return res; + } + + return device->setPreviewWindow(mAnw); +} + +status_t CameraHardwareInterfaceFlashControl::connectCameraDevice( + const String8& cameraId) { + sp<CameraHardwareInterface> device = + new CameraHardwareInterface(cameraId.string()); + + status_t res = device->initialize(mCameraModule); + if (res) { + ALOGE("%s: initializing camera %s failed", __FUNCTION__, + cameraId.string()); + return res; + } + + // need to set __get_memory in set_callbacks(). + device->setCallbacks(NULL, NULL, NULL, NULL); + + mParameters = device->getParameters(); + + int32_t width, height; + res = getSmallestSurfaceSize(&width, &height); + if (res) { + ALOGE("%s: failed to get smallest surface size for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + + res = initializePreviewWindow(device, width, height); + if (res) { + ALOGE("%s: failed to initialize preview window for camera %s", + __FUNCTION__, cameraId.string()); + return res; + } + + mCameraId = cameraId; + mDevice = device; + return OK; +} + +status_t CameraHardwareInterfaceFlashControl::disconnectCameraDevice() { + if (mDevice == NULL) { + return OK; + } + + mParameters.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_OFF); + mDevice->setParameters(mParameters); + mDevice->stopPreview(); + status_t res = native_window_api_disconnect(mAnw.get(), + NATIVE_WINDOW_API_CAMERA); + if (res) { + ALOGW("%s: native_window_api_disconnect failed: %s (%d)", + __FUNCTION__, strerror(-res), res); + } + mDevice->setPreviewWindow(NULL); + mDevice->release(); + + return OK; +} +// CameraHardwareInterfaceFlashControl implementation ends + +} diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h new file mode 100644 index 0000000..30f01f0 --- /dev/null +++ b/services/camera/libcameraservice/CameraFlashlight.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H +#define ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H + +#include "hardware/camera_common.h" +#include "utils/KeyedVector.h" +#include "utils/SortedVector.h" +#include "gui/GLConsumer.h" +#include "gui/Surface.h" +#include "common/CameraDeviceBase.h" +#include "device1/CameraHardwareInterface.h" + +namespace android { + +/** + * FlashControlBase is a base class for flash control. It defines the functions + * that a flash control for each camera module/device version should implement. + */ +class FlashControlBase : public virtual VirtualLightRefBase { + public: + virtual ~FlashControlBase(); + + // Whether a camera device has a flash unit. Calling this function may + // cause the torch mode to be turned off in HAL v1 devices. If + // previously-on torch mode is turned off, + // callbacks.torch_mode_status_change() should be invoked. + virtual status_t hasFlashUnit(const String8& cameraId, + bool *hasFlash) = 0; + + // set the torch mode to on or off. + virtual status_t setTorchMode(const String8& cameraId, + bool enabled) = 0; +}; + +/** + * CameraFlashlight can be used by camera service to control flashflight. + */ +class CameraFlashlight : public virtual VirtualLightRefBase { + public: + CameraFlashlight(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraFlashlight(); + + // Find all flash units. This must be called before other methods. All + // camera devices must be closed when it's called because HAL v1 devices + // need to be opened to query available flash modes. + status_t findFlashUnits(); + + // Whether a camera device has a flash unit. Before findFlashUnits() is + // called, this function always returns false. + bool hasFlashUnit(const String8& cameraId); + + // set the torch mode to on or off. + status_t setTorchMode(const String8& cameraId, bool enabled); + + // Notify CameraFlashlight that camera service is going to open a camera + // device. CameraFlashlight will free the resources that may cause the + // camera open to fail. Camera service must call this function before + // opening a camera device. + status_t prepareDeviceOpen(const String8& cameraId); + + // Notify CameraFlashlight that camera service has closed a camera + // device. CameraFlashlight may invoke callbacks for torch mode + // available depending on the implementation. + status_t deviceClosed(const String8& cameraId); + + private: + // create flashlight control based on camera module API and camera + // device API versions. + status_t createFlashlightControl(const String8& cameraId); + + // mLock should be locked. + bool hasFlashUnitLocked(const String8& cameraId); + + sp<FlashControlBase> mFlashControl; + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + SortedVector<String8> mOpenedCameraIds; + + // camera id -> if it has a flash unit + KeyedVector<String8, bool> mHasFlashlightMap; + bool mFlashlightMapInitialized; + + Mutex mLock; // protect CameraFlashlight API +}; + +/** + * Flash control for camera module v2.4 and above. + */ +class ModuleFlashControl : public FlashControlBase { + public: + ModuleFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~ModuleFlashControl(); + + // FlashControlBase + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + status_t setTorchMode(const String8& cameraId, bool enabled); + + private: + CameraModule *mCameraModule; + + Mutex mLock; +}; + +/** + * Flash control for camera module <= v2.3 and camera HAL v2-v3 + */ +class CameraDeviceClientFlashControl : public FlashControlBase { + public: + CameraDeviceClientFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraDeviceClientFlashControl(); + + // FlashControlBase + status_t setTorchMode(const String8& cameraId, bool enabled); + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + + private: + // connect to a camera device + status_t connectCameraDevice(const String8& cameraId); + // disconnect and free mDevice + status_t disconnectCameraDevice(); + + // initialize a surface + status_t initializeSurface(sp<CameraDeviceBase>& device, int32_t width, + int32_t height); + + // submit a request to enable the torch mode + status_t submitTorchEnabledRequest(); + + // get the smallest surface size of IMPLEMENTATION_DEFINED + status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width, + int32_t *height); + + // protected by mLock + status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash); + + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + String8 mCameraId; + bool mTorchEnabled; + CameraMetadata *mMetadata; + // WORKAROUND: will be set to true for HAL v2 devices where + // setStreamingRequest() needs to be call for torch mode settings to + // take effect. + bool mStreaming; + + sp<CameraDeviceBase> mDevice; + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<GLConsumer> mSurfaceTexture; + sp<ANativeWindow> mAnw; + int32_t mStreamId; + + Mutex mLock; +}; + +/** + * Flash control for camera module <= v2.3 and camera HAL v1 + */ +class CameraHardwareInterfaceFlashControl : public FlashControlBase { + public: + CameraHardwareInterfaceFlashControl(CameraModule& cameraModule, + const camera_module_callbacks_t& callbacks); + virtual ~CameraHardwareInterfaceFlashControl(); + + // FlashControlBase + status_t setTorchMode(const String8& cameraId, bool enabled); + status_t hasFlashUnit(const String8& cameraId, bool *hasFlash); + + private: + // connect to a camera device + status_t connectCameraDevice(const String8& cameraId); + + // disconnect and free mDevice + status_t disconnectCameraDevice(); + + // initialize the preview window + status_t initializePreviewWindow(sp<CameraHardwareInterface> device, + int32_t width, int32_t height); + + // start preview and enable torch + status_t startPreviewAndTorch(); + + // get the smallest surface + status_t getSmallestSurfaceSize(int32_t *width, int32_t *height); + + // protected by mLock + status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash); + + CameraModule *mCameraModule; + const camera_module_callbacks_t *mCallbacks; + sp<CameraHardwareInterface> mDevice; + String8 mCameraId; + CameraParameters mParameters; + bool mTorchEnabled; + + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<GLConsumer> mSurfaceTexture; + sp<ANativeWindow> mAnw; + + Mutex mLock; +}; + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 1232c32..e9c96c6 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -17,9 +17,14 @@ #define LOG_TAG "CameraService" //#define LOG_NDEBUG 0 +#include <algorithm> +#include <climits> #include <stdio.h> -#include <string.h> +#include <cstring> +#include <ctime> +#include <string> #include <sys/types.h> +#include <inttypes.h> #include <pthread.h> #include <binder/AppOpsManager.h> @@ -27,6 +32,7 @@ #include <binder/IServiceManager.h> #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> +#include <binder/ProcessInfoService.h> #include <cutils/atomic.h> #include <cutils/properties.h> #include <gui/Surface.h> @@ -45,7 +51,6 @@ #include "CameraService.h" #include "api1/CameraClient.h" #include "api1/Camera2Client.h" -#include "api_pro/ProCamera2Client.h" #include "api2/CameraDeviceClient.h" #include "utils/CameraTraces.h" #include "CameraDeviceFactory.h" @@ -66,25 +71,48 @@ static void setLogLevel(int level) { // ---------------------------------------------------------------------------- -static int getCallingPid() { - return IPCThreadState::self()->getCallingPid(); -} - -static int getCallingUid() { - return IPCThreadState::self()->getCallingUid(); -} - extern "C" { static void camera_device_status_change( const struct camera_module_callbacks* callbacks, int camera_id, int new_status) { sp<CameraService> cs = const_cast<CameraService*>( + static_cast<const CameraService*>(callbacks)); + + cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id), + static_cast<camera_device_status_t>(new_status)); +} + +static void torch_mode_status_change( + const struct camera_module_callbacks* callbacks, + const char* camera_id, + int new_status) { + if (!callbacks || !camera_id) { + ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__, + callbacks, camera_id); + } + sp<CameraService> cs = const_cast<CameraService*>( static_cast<const CameraService*>(callbacks)); - cs->onDeviceStatusChanged( - camera_id, - new_status); + ICameraServiceListener::TorchStatus status; + switch (new_status) { + case TORCH_MODE_STATUS_NOT_AVAILABLE: + status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE; + break; + case TORCH_MODE_STATUS_AVAILABLE_OFF: + status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF; + break; + case TORCH_MODE_STATUS_AVAILABLE_ON: + status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON; + break; + default: + ALOGE("Unknown torch status %d", new_status); + return; + } + + cs->onTorchStatusChanged( + String8(camera_id), + status); } } // extern "C" @@ -94,50 +122,89 @@ static void camera_device_status_change( // should be ok for now. static CameraService *gCameraService; -CameraService::CameraService() - :mSoundRef(0), mModule(0) -{ +CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), + mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) { ALOGI("CameraService started (pid=%d)", getpid()); gCameraService = this; - for (size_t i = 0; i < MAX_CAMERAS; ++i) { - mStatusList[i] = ICameraServiceListener::STATUS_PRESENT; - } - this->camera_device_status_change = android::camera_device_status_change; + this->torch_mode_status_change = android::torch_mode_status_change; + + mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock); } void CameraService::onFirstRef() { - LOG1("CameraService::onFirstRef"); + ALOGI("CameraService process starting"); BnCameraService::onFirstRef(); + camera_module_t *rawModule; if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, - (const hw_module_t **)&mModule) < 0) { + (const hw_module_t **)&rawModule) < 0) { ALOGE("Could not load camera HAL module"); mNumberOfCameras = 0; } else { - ALOGI("Loaded \"%s\" camera module", mModule->common.name); - mNumberOfCameras = mModule->get_number_of_cameras(); - if (mNumberOfCameras > MAX_CAMERAS) { - ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).", - mNumberOfCameras, MAX_CAMERAS); - mNumberOfCameras = MAX_CAMERAS; + mModule = new CameraModule(rawModule); + ALOGI("Loaded \"%s\" camera module", mModule->getModuleName()); + mNumberOfCameras = mModule->getNumberOfCameras(); + + mFlashlight = new CameraFlashlight(*mModule, *this); + status_t res = mFlashlight->findFlashUnits(); + if (res) { + // impossible because we haven't open any camera devices. + ALOGE("Failed to find flash units."); } + for (int i = 0; i < mNumberOfCameras; i++) { - setCameraFree(i); + String8 cameraId = String8::format("%d", i); + + // Defaults to use for cost and conflicting devices + int cost = 100; + char** conflicting_devices = nullptr; + size_t conflicting_devices_length = 0; + + // If using post-2.4 module version, query the cost + conflicting devices from the HAL + if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) { + struct camera_info info; + status_t rc = mModule->getCameraInfo(i, &info); + if (rc == NO_ERROR) { + cost = info.resource_cost; + conflicting_devices = info.conflicting_devices; + conflicting_devices_length = info.conflicting_devices_length; + } else { + ALOGE("%s: Received error loading camera info for device %d, cost and" + " conflicting devices fields set to defaults for this device.", + __FUNCTION__, i); + } + } + + std::set<String8> conflicting; + for (size_t i = 0; i < conflicting_devices_length; i++) { + conflicting.emplace(String8(conflicting_devices[i])); + } + + // Initialize state for each camera device + { + Mutex::Autolock lock(mCameraStatesLock); + mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost, + conflicting)); + } + + if (mFlashlight->hasFlashUnit(cameraId)) { + mTorchStatusMap.add(cameraId, + ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); + } } - if (mModule->common.module_api_version >= - CAMERA_MODULE_API_VERSION_2_1) { - mModule->set_callbacks(this); + if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) { + mModule->setCallbacks(this); } VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) { + if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) { setUpVendorTags(); } @@ -146,80 +213,111 @@ void CameraService::onFirstRef() } CameraService::~CameraService() { - for (int i = 0; i < mNumberOfCameras; i++) { - if (mBusy[i]) { - ALOGE("camera %d is still in use in destructor!", i); - } + if (mModule) { + delete mModule; + mModule = nullptr; } - VendorTagDescriptor::clearGlobalVendorTagDescriptor(); - gCameraService = NULL; + gCameraService = nullptr; } -void CameraService::onDeviceStatusChanged(int cameraId, - int newStatus) -{ +void CameraService::onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus) { ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__, cameraId, newStatus); - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { + String8 id = String8::format("%d", cameraId); + std::shared_ptr<CameraState> state = getCameraState(id); + + if (state == nullptr) { ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId); return; } - if ((int)getStatus(cameraId) == newStatus) { - ALOGE("%s: State transition to the same status 0x%x not allowed", - __FUNCTION__, (uint32_t)newStatus); + ICameraServiceListener::Status oldStatus = state->getStatus(); + + if (oldStatus == static_cast<ICameraServiceListener::Status>(newStatus)) { + ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus); return; } - /* don't do this in updateStatus - since it is also called from connect and we could get into a deadlock */ if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) { - Vector<sp<BasicClient> > clientsToDisconnect; + sp<BasicClient> clientToDisconnect; { - Mutex::Autolock al(mServiceLock); - - /* Remove cached parameters from shim cache */ - mShimParams.removeItem(cameraId); - - /* Find all clients that we need to disconnect */ - sp<BasicClient> client = mClient[cameraId].promote(); - if (client.get() != NULL) { - clientsToDisconnect.push_back(client); - } - - int i = cameraId; - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - sp<ProClient> cl = mProClientList[i][j].promote(); - if (cl != NULL) { - clientsToDisconnect.push_back(cl); - } - } - } + // Don't do this in updateStatus to avoid deadlock over mServiceLock + Mutex::Autolock lock(mServiceLock); - /* now disconnect them. don't hold the lock - or we can get into a deadlock */ + // Set the device status to NOT_PRESENT, clients will no longer be able to connect + // to this device until the status changes + updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id); - for (size_t i = 0; i < clientsToDisconnect.size(); ++i) { - sp<BasicClient> client = clientsToDisconnect[i]; + // Remove cached shim parameters + state->setShimParams(CameraParameters()); - client->disconnect(); - /** - * The remote app will no longer be able to call methods on the - * client since the client PID will be reset to 0 - */ + // Remove the client from the list of active clients + clientToDisconnect = removeClientLocked(id); + + // Notify the client of disconnection + clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras{}); + } + + ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL", + __FUNCTION__, id.string()); + + // Disconnect client + if (clientToDisconnect.get() != nullptr) { + // Ensure not in binder RPC so client disconnect PID checks work correctly + LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(), + "onDeviceStatusChanged must be called from the camera service process!"); + clientToDisconnect->disconnect(); } - ALOGV("%s: After unplug, disconnected %zu clients", - __FUNCTION__, clientsToDisconnect.size()); + } else { + updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id); } - updateStatus( - static_cast<ICameraServiceListener::Status>(newStatus), cameraId); +} +void CameraService::onTorchStatusChanged(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + Mutex::Autolock al(mTorchStatusMutex); + onTorchStatusChangedLocked(cameraId, newStatus); } +void CameraService::onTorchStatusChangedLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus) { + ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d", + __FUNCTION__, cameraId.string(), newStatus); + + ICameraServiceListener::TorchStatus status; + status_t res = getTorchStatusLocked(cameraId, &status); + if (res) { + ALOGE("%s: cannot get torch status of camera %s", cameraId.string()); + return; + } + if (status == newStatus) { + ALOGE("%s: Torch state transition to the same status 0x%x not allowed", + __FUNCTION__, (uint32_t)newStatus); + return; + } + + res = setTorchStatusLocked(cameraId, newStatus); + if (res) { + ALOGE("%s: Failed to set the torch status", __FUNCTION__, + (uint32_t)newStatus); + return; + } + + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& i : mListenerList) { + i->onTorchStatusChanged(newStatus, String16{cameraId}); + } + } +} + + int32_t CameraService::getNumberOfCameras() { return mNumberOfCameras; } @@ -236,12 +334,21 @@ status_t CameraService::getCameraInfo(int cameraId, struct camera_info info; status_t rc = filterGetInfoErrorCode( - mModule->get_camera_info(cameraId, &info)); + mModule->getCameraInfo(cameraId, &info)); cameraInfo->facing = info.facing; cameraInfo->orientation = info.orientation; return rc; } +int CameraService::cameraIdToInt(const String8& cameraId) { + errno = 0; + size_t pos = 0; + int ret = stoi(std::string{cameraId.string()}, &pos); + if (errno != 0 || pos != cameraId.size()) { + return -1; + } + return ret; +} status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) { status_t ret = OK; @@ -347,7 +454,7 @@ status_t CameraService::getCameraCharacteristics(int cameraId, int facing; status_t ret = OK; - if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0 || + if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0 || getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1 ) { /** * Backwards compatibility mode for old HALs: @@ -368,13 +475,61 @@ status_t CameraService::getCameraCharacteristics(int cameraId, * Normal HAL 2.1+ codepath. */ struct camera_info info; - ret = filterGetInfoErrorCode(mModule->get_camera_info(cameraId, &info)); + ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info)); *cameraInfo = info.static_camera_characteristics; } return ret; } +int CameraService::getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +int CameraService::getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +String8 CameraService::getFormattedCurrentTime() { + time_t now = time(nullptr); + char formattedTime[64]; + strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now)); + return String8(formattedTime); +} + +int CameraService::getCameraPriorityFromProcState(int procState) { + // Find the priority for the camera usage based on the process state. Higher priority clients + // win for evictions. + // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose + // the camera to the top/foreground applications. + switch(procState) { + case PROCESS_STATE_TOP: // User visible + return 100; + case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground + return 90; + case PROCESS_STATE_PERSISTENT: // Persistent system services + case PROCESS_STATE_PERSISTENT_UI: + return 80; + case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes + return 70; + case PROCESS_STATE_BACKUP: // Everything else + case PROCESS_STATE_HEAVY_WEIGHT: + case PROCESS_STATE_SERVICE: + case PROCESS_STATE_RECEIVER: + case PROCESS_STATE_HOME: + case PROCESS_STATE_LAST_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY: + case PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + case PROCESS_STATE_CACHED_EMPTY: + return 1; + case PROCESS_STATE_NONEXISTENT: + return -1; + default: + ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__); + return -1; + } +} + status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { if (!mModule) { ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); @@ -387,12 +542,12 @@ status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescript int CameraService::getDeviceVersion(int cameraId, int* facing) { struct camera_info info; - if (mModule->get_camera_info(cameraId, &info) != OK) { + if (mModule->getCameraInfo(cameraId, &info) != OK) { return -1; } int deviceVersion; - if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_0) { + if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) { deviceVersion = info.device_version; } else { deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; @@ -405,19 +560,6 @@ int CameraService::getDeviceVersion(int cameraId, int* facing) { return deviceVersion; } -status_t CameraService::filterOpenErrorCode(status_t err) { - switch(err) { - case NO_ERROR: - case -EBUSY: - case -EINVAL: - case -EUSERS: - return err; - default: - break; - } - return -ENODEV; -} - status_t CameraService::filterGetInfoErrorCode(status_t err) { switch(err) { case NO_ERROR: @@ -433,13 +575,13 @@ bool CameraService::setUpVendorTags() { vendor_tag_ops_t vOps = vendor_tag_ops_t(); // Check if vendor operations have been implemented - if (mModule->get_vendor_tag_ops == NULL) { + if (!mModule->isVendorTagDefined()) { ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__); return false; } ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); - mModule->get_vendor_tag_ops(&vOps); + mModule->getVendorTagOps(&vOps); ATRACE_END(); // Ensure all vendor operations are present @@ -467,54 +609,90 @@ bool CameraService::setUpVendorTags() { return true; } -status_t CameraService::initializeShimMetadata(int cameraId) { - int pid = getCallingPid(); - int uid = getCallingUid(); - status_t ret = validateConnect(cameraId, uid); - if (ret != OK) { - // Error already logged by callee - return ret; - } +status_t CameraService::makeClient(const sp<CameraService>& cameraService, + const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp<BasicClient>* client) { - bool needsNewClient = false; - sp<Client> client; + // TODO: Update CameraClients + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } - String16 internalPackageName("media"); - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - if (mClient[cameraId] != NULL) { - client = static_cast<Client*>(mClient[cameraId].promote().get()); - } - if (client == NULL) { - needsNewClient = true; - ret = connectHelperLocked(/*out*/client, - /*cameraClient*/NULL, // Empty binder callbacks - cameraId, - internalPackageName, - uid, - pid); - - if (ret != OK) { - // Error already logged by callee - return ret; + if (halVersion < 0 || halVersion == deviceVersion) { + // Default path: HAL version is unspecified by caller, create CameraClient + // based on device version reported by the HAL. + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, getpid(), legacyMode); + } else { // Camera2 API route + ALOGW("Camera using old HAL version: %d", deviceVersion); + return -EOPNOTSUPP; + } + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: + if (effectiveApiLevel == API_1) { // Camera1 API route + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new Camera2Client(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { // Camera2 API route + sp<ICameraDeviceCallbacks> tmp = + static_cast<ICameraDeviceCallbacks*>(cameraCb.get()); + *client = new CameraDeviceClient(cameraService, tmp, packageName, id, + facing, clientPid, clientUid, servicePid); } + break; + default: + // Should not be reachable + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return INVALID_OPERATION; } - - if (client == NULL) { - ALOGE("%s: Could not connect to client camera device.", __FUNCTION__); - return BAD_VALUE; + } else { + // A particular HAL version is requested by caller. Create CameraClient + // based on the requested HAL version. + if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && + halVersion == CAMERA_DEVICE_API_VERSION_1_0) { + // Only support higher HAL version device opened as HAL1.0 device. + sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get()); + *client = new CameraClient(cameraService, tmp, packageName, id, facing, + clientPid, clientUid, servicePid, legacyMode); + } else { + // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. + ALOGE("Invalid camera HAL version %x: HAL %x device can only be" + " opened as HAL %x device", halVersion, deviceVersion, + CAMERA_DEVICE_API_VERSION_1_0); + return INVALID_OPERATION; } - - String8 rawParams = client->getParameters(); - CameraParameters params(rawParams); - mShimParams.add(cameraId, params); } + return NO_ERROR; +} - // Close client if one was opened solely for this call - if (needsNewClient) { - client->disconnect(); +status_t CameraService::initializeShimMetadata(int cameraId) { + int uid = getCallingUid(); + + String16 internalPackageName("media"); + String8 id = String8::format("%d", cameraId); + status_t ret = NO_ERROR; + sp<Client> tmp = nullptr; + if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id, + static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1, + false, true, tmp)) != NO_ERROR) { + ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret)); + return ret; } - return OK; + return NO_ERROR; } status_t CameraService::getLegacyParametersLazy(int cameraId, @@ -530,42 +708,55 @@ status_t CameraService::getLegacyParametersLazy(int cameraId, return BAD_VALUE; } - ssize_t index = -1; - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - // Release service lock so initializeShimMetadata can be called correctly. + String8 id = String8::format("%d", cameraId); - if (index >= 0) { - *parameters = mShimParams[index]; + // Check if we already have parameters + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; } - } - - if (index < 0) { - int64_t token = IPCThreadState::self()->clearCallingIdentity(); - ret = initializeShimMetadata(cameraId); - IPCThreadState::self()->restoreCallingIdentity(token); - if (ret != OK) { - // Error already logged by callee - return ret; + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } + } - { // Scope for service lock - Mutex::Autolock lock(mServiceLock); - index = mShimParams.indexOfKey(cameraId); - - LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized"); + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + ret = initializeShimMetadata(cameraId); + IPCThreadState::self()->restoreCallingIdentity(token); + if (ret != NO_ERROR) { + // Error already logged by callee + return ret; + } - *parameters = mShimParams[index]; + // Check for parameters again + { + // Scope for service lock + Mutex::Autolock lock(mServiceLock); + auto cameraState = getCameraState(id); + if (cameraState == nullptr) { + ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string()); + return BAD_VALUE; + } + CameraParameters p = cameraState->getShimParams(); + if (!p.isEmpty()) { + *parameters = p; + return NO_ERROR; } } - return OK; + ALOGE("%s: Parameters were not initialized, or were empty. Device may not be present.", + __FUNCTION__); + return INVALID_OPERATION; } -status_t CameraService::validateConnect(int cameraId, - /*inout*/ - int& clientUid) const { +status_t CameraService::validateConnectLocked(const String8& cameraId, /*inout*/int& clientUid) + const { int callingPid = getCallingPid(); @@ -574,160 +765,251 @@ status_t CameraService::validateConnect(int cameraId, } else { // We only trust our own process to forward client UIDs if (callingPid != getpid()) { - ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)", + ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)", callingPid); return PERMISSION_DENIED; } } if (!mModule) { - ALOGE("Camera HAL module not loaded"); + ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)", + callingPid); return -ENODEV; } - if (cameraId < 0 || cameraId >= mNumberOfCameras) { - ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", - callingPid, cameraId); + if (getCameraState(cameraId) == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); return -ENODEV; } + // Check device policy for this camera char value[PROPERTY_VALUE_MAX]; - property_get("sys.secpolicy.camera.disabled", value, "0"); + char key[PROPERTY_KEY_MAX]; + int clientUserId = multiuser_get_user_id(clientUid); + snprintf(key, PROPERTY_KEY_MAX, "sys.secpolicy.camera.off_%d", clientUserId); + property_get(key, value, "0"); if (strcmp(value, "1") == 0) { // Camera is disabled by DevicePolicyManager. - ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device " + "policy)", callingPid, cameraId.string()); return -EACCES; } - ICameraServiceListener::Status currentStatus = getStatus(cameraId); + // Only allow clients who are being used by the current foreground device user. + if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) { + ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground " + "device user)", callingPid); + return PERMISSION_DENIED; + } + + return checkIfDeviceIsUsable(cameraId); +} + +status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const { + auto cameraState = getCameraState(cameraId); + int callingPid = getCallingPid(); + if (cameraState == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid, + cameraId.string()); + return -ENODEV; + } + + ICameraServiceListener::Status currentStatus = cameraState->getStatus(); if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) { - ALOGI("Camera is not plugged in," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)", + callingPid, cameraId.string()); return -ENODEV; } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) { - ALOGI("Camera is enumerating," - " connect X (pid %d) rejected", callingPid); + ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)", + callingPid, cameraId.string()); return -EBUSY; } - // Else don't check for STATUS_NOT_AVAILABLE. - // -- It's done implicitly in canConnectUnsafe /w the mBusy array - return OK; + return NO_ERROR; } -bool CameraService::canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp<IBinder>& remoteCallback, - sp<BasicClient> &client) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); +void CameraService::finishConnectLocked(const sp<BasicClient>& client, + const CameraService::DescriptorPtr& desc) { - if (mClient[cameraId] != 0) { - client = mClient[cameraId].promote(); - if (client != 0) { - if (remoteCallback == client->getRemote()) { - LOG1("CameraService::connect X (pid %d) (the same client)", - callingPid); - return true; - } else { - // TODOSC: need to support 1 regular client, - // multiple shared clients here - ALOGW("CameraService::connect X (pid %d) rejected" - " (existing client).", callingPid); - return false; - } + // Make a descriptor for the incoming client + auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc); + auto evicted = mActiveClientManager.addAndEvict(clientDescriptor); + + logConnected(desc->getKey(), static_cast<int>(desc->getOwnerId()), + String8(client->getPackageName())); + + if (evicted.size() > 0) { + // This should never happen - clients should already have been removed in disconnect + for (auto& i : evicted) { + ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect", + __FUNCTION__, i->getKey().string()); } - mClient[cameraId].clear(); - } - - /* - mBusy is set to false as the last step of the Client destructor, - after which it is guaranteed that the Client destructor has finished ( - including any inherited destructors) - - We only need this for a Client subclasses since we don't allow - multiple Clents to be opened concurrently, but multiple BasicClient - would be fine - */ - if (mBusy[cameraId]) { - ALOGW("CameraService::connect X (pid %d, \"%s\") rejected" - " (camera %d is still busy).", callingPid, - clientName8.string(), cameraId); - return false; - } - return true; + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly", + __FUNCTION__); + } } -status_t CameraService::connectHelperLocked( +status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName, /*out*/ - sp<Client>& client, - /*in*/ - const sp<ICameraClient>& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion, - bool legacyMode) { + sp<BasicClient>* client, + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) { - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + status_t ret = NO_ERROR; + std::vector<sp<BasicClient>> evictedClients; + DescriptorPtr clientDescriptor; + { + if (effectiveApiLevel == API_1) { + // If we are using API1, any existing client for this camera ID with the same remote + // should be returned rather than evicted to allow MediaRecorder to work properly. + + auto current = mActiveClientManager.get(cameraId); + if (current != nullptr) { + auto clientSp = current->getValue(); + if (clientSp.get() != nullptr) { // should never be needed + if (clientSp->getRemote() == remoteCallback) { + ALOGI("CameraService::connect X (PID %d) (second call from same" + "app binder, returning the same client)", clientPid); + *client = clientSp; + return NO_ERROR; + } + } + } + } - if (halVersion < 0 || halVersion == deviceVersion) { - // Default path: HAL version is unspecified by caller, create CameraClient - // based on device version reported by the HAL. - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new Camera2Client(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); + // Return error if the device was unplugged or removed by the HAL for some reason + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } + + // Get current active client PIDs + std::vector<int> ownerPids(mActiveClientManager.getAllOwners()); + ownerPids.push_back(clientPid); + + // Use the value +PROCESS_STATE_NONEXISTENT, to avoid taking + // address of PROCESS_STATE_NONEXISTENT as a reference argument + // for the vector constructor. PROCESS_STATE_NONEXISTENT does + // not have an out-of-class definition. + std::vector<int> priorities(ownerPids.size(), +PROCESS_STATE_NONEXISTENT); + + // Get priorites of all active PIDs + ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0], + /*out*/&priorities[0]); + + // Update all active clients' priorities + std::map<int,int> pidToPriorityMap; + for (size_t i = 0; i < ownerPids.size() - 1; i++) { + pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i])); + } + mActiveClientManager.updatePriorities(pidToPriorityMap); + + // Get state for the given cameraId + auto state = getCameraState(cameraId); + if (state == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)", + clientPid, cameraId.string()); return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; } - } else { - // A particular HAL version is requested by caller. Create CameraClient - // based on the requested HAL version. - if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && - halVersion == CAMERA_DEVICE_API_VERSION_1_0) { - // Only support higher HAL version device opened as HAL1.0 device. - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), legacyMode); - } else { - // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. - ALOGE("Invalid camera HAL version %x: HAL %x device can only be" - " opened as HAL %x device", halVersion, deviceVersion, - CAMERA_DEVICE_API_VERSION_1_0); - return INVALID_OPERATION; + + // Make descriptor for incoming client + clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId, + sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()), + state->getConflicting(), + getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid); + + // Find clients that would be evicted + auto evicted = mActiveClientManager.wouldEvict(clientDescriptor); + + // If the incoming client was 'evicted,' higher priority clients have the camera in the + // background, so we cannot do evictions + if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) { + ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher" + " priority).", clientPid); + + sp<BasicClient> clientSp = clientDescriptor->getValue(); + String8 curTime = getFormattedCurrentTime(); + auto incompatibleClients = + mActiveClientManager.getIncompatibleClients(clientDescriptor); + + String8 msg = String8::format("%s : DENIED connect device %s client for package %s " + "(PID %d, priority %d)", curTime.string(), + cameraId.string(), packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1])); + + for (auto& i : incompatibleClients) { + msg.appendFormat("\n - Blocked by existing device %s client for package %s" + "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(), + String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(), + i->getPriority()); + } + + // Log the client's attempt + mEventLog.add(msg); + + return -EBUSY; + } + + for (auto& i : evicted) { + sp<BasicClient> clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__); + + // TODO: Remove this + LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list", + __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + + ALOGE("CameraService::connect evicting conflicting client for camera ID %s", + i->getKey().string()); + evictedClients.push_back(clientSp); + + String8 curTime = getFormattedCurrentTime(); + + // Log the clients evicted + mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" + PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for " + "package %s (PID %d, priority %" PRId32 ")", curTime.string(), + i->getKey().string(), String8{clientSp->getPackageName()}.string(), + i->getOwnerId(), i->getPriority(), cameraId.string(), + packageName.string(), clientPid, + getCameraPriorityFromProcState(priorities[priorities.size() - 1]))); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); } } - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); + + // Clear caller identity temporarily so client disconnect PID checks work correctly + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + + // Destroy evicted clients + for (auto& i : evictedClients) { + // Disconnect is blocking, and should only have returned when HAL has cleaned up + i->disconnect(); // Clients will remove themselves from the active client list here } + evictedClients.clear(); - mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, - getpid()); + IPCThreadState::self()->restoreCallingIdentity(token); - return OK; + // Once clients have been disconnected, relock + mServiceLock.lock(); + + // Check again if the device was unplugged or something while we weren't holding mServiceLock + if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) { + return ret; + } + + *partial = clientDescriptor; + return NO_ERROR; } status_t CameraService::connect( @@ -738,47 +1020,18 @@ status_t CameraService::connect( /*out*/ sp<ICamera>& device) { - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; - } - - - sp<Client> client; - { - Mutex::Autolock lock(mServiceLock); - sp<BasicClient> clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraClient), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast<Client*>(clientTmp.get()); - return OK; - } - - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid); - if (status != OK) { - return status; - } + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<Client> client = nullptr; + ret = connectHelper<ICameraClient,Client>(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED, + clientPackageName, clientUid, API_1, false, false, /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } status_t CameraService::connectLegacy( @@ -789,8 +1042,9 @@ status_t CameraService::connectLegacy( /*out*/ sp<ICamera>& device) { + int apiVersion = mModule->getModuleApiVersion(); if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED && - mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_3) { + apiVersion < CAMERA_MODULE_API_VERSION_2_3) { /* * Either the HAL version is unspecified in which case this just creates * a camera client selected by the latest device version, or @@ -798,219 +1052,138 @@ status_t CameraService::connectLegacy( * the open_legacy call */ ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!", - __FUNCTION__, mModule->common.module_api_version); + __FUNCTION__, apiVersion); return INVALID_OPERATION; } - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<Client> client = nullptr; + ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName, + clientUid, API_1, true, false, /*out*/client); - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; + if(ret != NO_ERROR) { + return ret; } - sp<Client> client; - { - Mutex::Autolock lock(mServiceLock); - sp<BasicClient> clientTmp; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraClient), - /*out*/clientTmp)) { - return -EBUSY; - } else if (client.get() != NULL) { - device = static_cast<Client*>(clientTmp.get()); - return OK; - } + device = client; + return NO_ERROR; +} - status = connectHelperLocked(/*out*/client, - cameraClient, - cameraId, - clientPackageName, - clientUid, - callingPid, - halVersion, - /*legacyMode*/true); - if (status != OK) { - return status; - } +status_t CameraService::connectDevice( + const sp<ICameraDeviceCallbacks>& cameraCb, + int cameraId, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp<ICameraDeviceUser>& device) { + + status_t ret = NO_ERROR; + String8 id = String8::format("%d", cameraId); + sp<CameraDeviceClient> client = nullptr; + ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id, + CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false, + /*out*/client); + if(ret != NO_ERROR) { + return ret; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) device = client; - return OK; + return NO_ERROR; } -status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client, - const sp<IBinder>& remoteCallback) { - status_t status = client->initialize(mModule); - if (status != OK) { - ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); - return status; - } - if (remoteCallback != NULL) { - remoteCallback->linkToDeath(this); +status_t CameraService::setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder) { + if (enabled && clientBinder == NULL) { + ALOGE("%s: torch client binder is NULL", __FUNCTION__); + return -EINVAL; } - return OK; -} + String8 id = String8(cameraId.string()); -status_t CameraService::connectPro( - const sp<IProCameraCallbacks>& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp<IProCameraUser>& device) -{ - if (cameraCb == 0) { - ALOGE("%s: Callback must not be null", __FUNCTION__); - return BAD_VALUE; + // verify id is valid. + auto state = getCameraState(id); + if (state == nullptr) { + ALOGE("%s: camera id is invalid %s", id.string()); + return -EINVAL; } - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; + ICameraServiceListener::Status cameraStatus = state->getStatus(); + if (cameraStatus != ICameraServiceListener::STATUS_PRESENT && + cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) { + ALOGE("%s: camera id is invalid %s", id.string()); + return -EINVAL; } - sp<ProClient> client; { - Mutex::Autolock lock(mServiceLock); - { - sp<BasicClient> client; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraCb), - /*out*/client)) { - return -EBUSY; - } + Mutex::Autolock al(mTorchStatusMutex); + ICameraServiceListener::TorchStatus status; + status_t res = getTorchStatusLocked(id, &status); + if (res) { + ALOGE("%s: getting current torch status failed for camera %s", + __FUNCTION__, id.string()); + return -EINVAL; } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); - - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", - cameraId); - return -EOPNOTSUPP; - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new ProCamera2Client(this, cameraCb, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; - } - - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - return status; + if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) { + if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) { + ALOGE("%s: torch mode of camera %s is not available because " + "camera is in use", __FUNCTION__, id.string()); + return -EBUSY; + } else { + ALOGE("%s: torch mode of camera %s is not available due to " + "insufficient resources", __FUNCTION__, id.string()); + return -EUSERS; + } } - - mProClientList[cameraId].push(client); - - LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId, - getpid()); } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) - device = client; - return OK; -} - -status_t CameraService::connectDevice( - const sp<ICameraDeviceCallbacks>& cameraCb, - int cameraId, - const String16& clientPackageName, - int clientUid, - /*out*/ - sp<ICameraDeviceUser>& device) -{ - String8 clientName8(clientPackageName); - int callingPid = getCallingPid(); - - LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid, - clientName8.string(), cameraId); - - status_t status = validateConnect(cameraId, /*inout*/clientUid); - if (status != OK) { - return status; + status_t res = mFlashlight->setTorchMode(id, enabled); + if (res) { + ALOGE("%s: setting torch mode of camera %s to %d failed. %s (%d)", + __FUNCTION__, id.string(), enabled, strerror(-res), res); + return res; } - sp<CameraDeviceClient> client; { - Mutex::Autolock lock(mServiceLock); - { - sp<BasicClient> client; - if (!canConnectUnsafe(cameraId, clientPackageName, - IInterface::asBinder(cameraCb), - /*out*/client)) { - return -EBUSY; + // update the link to client's death + Mutex::Autolock al(mTorchClientMapMutex); + ssize_t index = mTorchClientMap.indexOfKey(id); + if (enabled) { + if (index == NAME_NOT_FOUND) { + mTorchClientMap.add(id, clientBinder); + } else { + const sp<IBinder> oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); + + mTorchClientMap.replaceValueAt(index, clientBinder); } + clientBinder->linkToDeath(this); + } else if (index != NAME_NOT_FOUND) { + sp<IBinder> oldBinder = mTorchClientMap.valueAt(index); + oldBinder->unlinkToDeath(this); } + } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); + return OK; +} - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - ALOGW("Camera using old HAL version: %d", deviceVersion); - return -EOPNOTSUPP; - // TODO: don't allow 2.0 Only allow 2.1 and higher - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - case CAMERA_DEVICE_API_VERSION_3_1: - case CAMERA_DEVICE_API_VERSION_3_2: - client = new CameraDeviceClient(this, cameraCb, clientPackageName, - cameraId, facing, callingPid, clientUid, getpid()); +void CameraService::notifySystemEvent(int eventId, int arg0) { + switch(eventId) { + case ICameraService::USER_SWITCHED: { + doUserSwitch(/*newUserId*/arg0); break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; } - - status_t status = connectFinishUnsafe(client, client->getRemote()); - if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - return status; + case ICameraService::NO_EVENT: + default: { + ALOGW("%s: Received invalid system event from system_server: %d", __FUNCTION__, + eventId); + break; } - - LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId, - getpid()); - - mClient[cameraId] = client; } - // important: release the mutex here so the client can call back - // into the service from its destructor (can be at the end of the call) - - device = client; - return OK; } - status_t CameraService::addListener( const sp<ICameraServiceListener>& listener) { ALOGV("%s: Add listener %p", __FUNCTION__, listener.get()); @@ -1022,30 +1195,45 @@ status_t CameraService::addListener( Mutex::Autolock lock(mServiceLock); - Vector<sp<ICameraServiceListener> >::iterator it, end; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { - ALOGW("%s: Tried to add listener %p which was already subscribed", - __FUNCTION__, listener.get()); - return ALREADY_EXISTS; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto& it : mListenerList) { + if (IInterface::asBinder(it) == IInterface::asBinder(listener)) { + ALOGW("%s: Tried to add listener %p which was already subscribed", + __FUNCTION__, listener.get()); + return ALREADY_EXISTS; + } } + + mListenerList.push_back(listener); } - mListenerList.push_back(listener); /* Immediately signal current status to this listener only */ { - Mutex::Autolock m(mStatusMutex) ; - int numCams = getNumberOfCameras(); - for (int i = 0; i < numCams; ++i) { - listener->onStatusChanged(mStatusList[i], i); + Mutex::Autolock lock(mCameraStatesLock); + for (auto& i : mCameraStates) { + // TODO: Update binder to use String16 for camera IDs and remove; + int id = cameraIdToInt(i.first); + if (id == -1) continue; + + listener->onStatusChanged(i.second->getStatus(), id); + } + } + + /* Immediately signal current torch status to this listener only */ + { + Mutex::Autolock al(mTorchStatusMutex); + for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) { + String16 id = String16(mTorchStatusMap.keyAt(i).string()); + listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id); } } return OK; } -status_t CameraService::removeListener( - const sp<ICameraServiceListener>& listener) { + +status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) { ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get()); if (listener == 0) { @@ -1055,11 +1243,13 @@ status_t CameraService::removeListener( Mutex::Autolock lock(mServiceLock); - Vector<sp<ICameraServiceListener> >::iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { - mListenerList.erase(it); - return OK; + { + Mutex::Autolock lock(mStatusListenerLock); + for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { + if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) { + mListenerList.erase(it); + return OK; + } } } @@ -1069,10 +1259,7 @@ status_t CameraService::removeListener( return BAD_VALUE; } -status_t CameraService::getLegacyParameters( - int cameraId, - /*out*/ - String16* parameters) { +status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) { ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId); if (parameters == NULL) { @@ -1127,6 +1314,7 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } case CAMERA_DEVICE_API_VERSION_3_2: + case CAMERA_DEVICE_API_VERSION_3_3: ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly", __FUNCTION__, cameraId); return OK; @@ -1141,140 +1329,177 @@ status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { return OK; } -void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) { - int callingPid = getCallingPid(); - LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); - - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. +void CameraService::removeByClient(const BasicClient* client) { Mutex::Autolock lock(mServiceLock); + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == client) { + mActiveClientManager.remove(i); + } + } +} - int outIndex; - sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex); +bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) { + const int callingPid = getCallingPid(); + const int servicePid = getpid(); + bool ret = false; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr<AutoConditionLock> lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper); - if (client != 0) { - // Found our camera, clear and leave. - LOG1("removeClient: clear camera %d", outIndex); - sp<IBinder> remote = client->getRemote(); - if (remote != NULL) { - remote->unlinkToDeath(this); + std::vector<sp<BasicClient>> evicted; + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__); + mActiveClientManager.remove(i); + continue; + } + if (remote == clientSp->getRemote() && (callingPid == servicePid || + callingPid == clientSp->getClientPid())) { + mActiveClientManager.remove(i); + evicted.push_back(clientSp); + + // Notify the client of disconnection + clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED, + CaptureResultExtras()); + } } - mClient[outIndex].clear(); - } else { - - sp<ProClient> clientPro = findProClientUnsafe(remoteBinder); + // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking + // other clients from connecting in mServiceLockWrapper if held + mServiceLock.unlock(); - if (clientPro != NULL) { - // Found our camera, clear and leave. - LOG1("removeClient: clear pro %p", clientPro.get()); + // Do not clear caller identity, remote caller should be client proccess - IInterface::asBinder(clientPro->getRemoteCallback())->unlinkToDeath(this); + for (auto& i : evicted) { + if (i.get() != nullptr) { + i->disconnect(); + ret = true; + } } - } - LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid); -} - -sp<CameraService::ProClient> CameraService::findProClientUnsafe( - const wp<IBinder>& cameraCallbacksRemote) -{ - sp<ProClient> clientPro; + // Reacquire mServiceLock + mServiceLock.lock(); - for (int i = 0; i < mNumberOfCameras; ++i) { - Vector<size_t> removeIdx; + } // lock is destroyed, allow further connect calls - for (size_t j = 0; j < mProClientList[i].size(); ++j) { - wp<ProClient> cl = mProClientList[i][j]; + return ret; +} - sp<ProClient> clStrong = cl.promote(); - if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) { - clientPro = clStrong; - break; - } else if (clStrong == NULL) { - // mark to clean up dead ptr - removeIdx.push(j); - } - } - // remove stale ptrs (in reverse so the indices dont change) - for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) { - mProClientList[i].removeAt(removeIdx[j]); +std::shared_ptr<CameraService::CameraState> CameraService::getCameraState( + const String8& cameraId) const { + std::shared_ptr<CameraState> state; + { + Mutex::Autolock lock(mCameraStatesLock); + auto iter = mCameraStates.find(cameraId); + if (iter != mCameraStates.end()) { + state = iter->second; } + } + return state; +} +sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8& cameraId) { + // Remove from active clients list + auto clientDescriptorPtr = mActiveClientManager.remove(cameraId); + if (clientDescriptorPtr == nullptr) { + ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__, + cameraId.string()); + return sp<BasicClient>{nullptr}; } - return clientPro; + return clientDescriptorPtr->getValue(); } -sp<CameraService::BasicClient> CameraService::findClientUnsafe( - const wp<IBinder>& cameraClient, int& outIndex) { - sp<BasicClient> client; +void CameraService::doUserSwitch(int newUserId) { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr<AutoConditionLock> lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper); - for (int i = 0; i < mNumberOfCameras; i++) { + if (newUserId <= 0) { + ALOGW("%s: Bad user ID %d given during user switch, resetting to default.", __FUNCTION__, + newUserId); + newUserId = DEFAULT_LAST_USER_ID; + } - // This happens when we have already disconnected (or this is - // just another unused camera). - if (mClient[i] == 0) continue; + mLastUserId = newUserId; - // Promote mClient. It can fail if we are called from this path: - // Client::~Client() -> disconnect() -> removeClientByRemote(). - client = mClient[i].promote(); + // Current user has switched, evict all current clients. + std::vector<sp<BasicClient>> evicted; + for (auto& i : mActiveClientManager.getAll()) { + auto clientSp = i->getValue(); - // Clean up stale client entry - if (client == NULL) { - mClient[i].clear(); + if (clientSp.get() == nullptr) { + ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__); continue; } - if (cameraClient == client->getRemote()) { - // Found our camera - outIndex = i; - return client; - } + evicted.push_back(clientSp); + + String8 curTime = getFormattedCurrentTime(); + + ALOGE("Evicting conflicting client for camera ID %s due to user change", + i->getKey().string()); + // Log the clients evicted + mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %" + PRId32 ", priority %" PRId32 ")\n - Evicted due to user switch.", + curTime.string(), i->getKey().string(), + String8{clientSp->getPackageName()}.string(), i->getOwnerId(), + i->getPriority())); + } - outIndex = -1; - return NULL; -} + // Do not hold mServiceLock while disconnecting clients, but retain the condition + // blocking other clients from connecting in mServiceLockWrapper if held. + mServiceLock.unlock(); -CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return mClient[cameraId].unsafe_get(); -} + // Clear caller identity temporarily so client disconnect PID checks work correctly + int64_t token = IPCThreadState::self()->clearCallingIdentity(); -Mutex* CameraService::getClientLockById(int cameraId) { - if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; - return &mClientLock[cameraId]; -} + for (auto& i : evicted) { + i->disconnect(); + } -sp<CameraService::BasicClient> CameraService::getClientByRemote( - const wp<IBinder>& cameraClient) { + IPCThreadState::self()->restoreCallingIdentity(token); - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. - sp<BasicClient> client; + // Reacquire mServiceLock + mServiceLock.lock(); +} - Mutex::Autolock lock(mServiceLock); +void CameraService::logDisconnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - int outIndex; - client = findClientUnsafe(cameraClient, outIndex); + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); +} + +void CameraService::logConnected(const String8& cameraId, int clientPid, + const String8& clientPackage) { - return client; + String8 curTime = getFormattedCurrentTime(); + // Log the clients evicted + mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)", + curTime.string(), cameraId.string(), clientPackage.string(), clientPid)); } -status_t CameraService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { +status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + + const int pid = getCallingPid(); + const int selfPid = getpid(); + // Permission checks switch (code) { case BnCameraService::CONNECT: - case BnCameraService::CONNECT_PRO: case BnCameraService::CONNECT_DEVICE: - case BnCameraService::CONNECT_LEGACY: - const int pid = getCallingPid(); - const int self_pid = getpid(); - if (pid != self_pid) { + case BnCameraService::CONNECT_LEGACY: { + if (pid != selfPid) { // we're called from a different process, do the real check if (!checkCallingPermission( String16("android.permission.CAMERA"))) { @@ -1285,29 +1510,26 @@ status_t CameraService::onTransact( } } break; + } + case BnCameraService::NOTIFY_SYSTEM_EVENT: { + if (pid != selfPid) { + // Ensure we're being called by system_server, or similar process with + // permissions to notify the camera service about system events + if (!checkCallingPermission( + String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) { + const int uid = getCallingUid(); + ALOGE("Permission Denial: cannot send updates to camera service about system" + " events from pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + break; + } } return BnCameraService::onTransact(code, data, reply, flags); } -// The reason we need this busy bit is a new CameraService::connect() request -// may come in while the previous Client's destructor has not been run or is -// still running. If the last strong reference of the previous Client is gone -// but the destructor has not been finished, we should not allow the new Client -// to be created because we need to wait for the previous Client to tear down -// the hardware first. -void CameraService::setCameraBusy(int cameraId) { - android_atomic_write(1, &mBusy[cameraId]); - - ALOGV("setCameraBusy cameraId=%d", cameraId); -} - -void CameraService::setCameraFree(int cameraId) { - android_atomic_write(0, &mBusy[cameraId]); - - ALOGV("setCameraFree cameraId=%d", cameraId); -} - // We share the media players for shutter and recording sound for all clients. // A reference count is kept to determine when we will actually release the // media players. @@ -1376,7 +1598,6 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, mRemoteCallback = cameraClient; - cameraService->setCameraBusy(cameraId); cameraService->loadSound(); LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); @@ -1398,7 +1619,7 @@ CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService, int cameraId, int cameraFacing, int clientPid, uid_t clientUid, int servicePid): - mClientPackageName(clientPackageName) + mClientPackageName(clientPackageName), mDisconnected(false) { mCameraService = cameraService; mRemoteBinder = remoteCallback; @@ -1417,14 +1638,38 @@ CameraService::BasicClient::~BasicClient() { } void CameraService::BasicClient::disconnect() { - ALOGV("BasicClient::disconnect"); - mCameraService->removeClientByRemote(mRemoteBinder); + if (mDisconnected) { + ALOGE("%s: Disconnect called on already disconnected client for device %d", __FUNCTION__, + mCameraId); + return; + } + mDisconnected = true;; + + mCameraService->removeByClient(this); + mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid, + String8(mClientPackageName)); + + sp<IBinder> remote = getRemote(); + if (remote != nullptr) { + remote->unlinkToDeath(mCameraService); + } finishCameraOps(); + ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid); + // client shouldn't be able to call into us anymore mClientPid = 0; } +String16 CameraService::BasicClient::getPackageName() const { + return mClientPackageName; +} + + +int CameraService::BasicClient::getClientPid() const { + return mClientPid; +} + status_t CameraService::BasicClient::startCameraOps() { int32_t res; // Notify app ops that the camera is not available @@ -1450,7 +1695,7 @@ status_t CameraService::BasicClient::startCameraOps() { // Transition device availability listeners from PRESENT -> NOT_AVAILABLE mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, - mCameraId); + String8::format("%d", mCameraId)); return OK; } @@ -1463,19 +1708,16 @@ status_t CameraService::BasicClient::finishCameraOps() { mClientPackageName); mOpsActive = false; - // Notify device availability listeners that this camera is available - // again + auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT, + ICameraServiceListener::STATUS_ENUMERATING}; - StatusVector rejectSourceStates; - rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); - rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); - - // Transition to PRESENT if the camera is not in either of above 2 - // states + // Transition to PRESENT if the camera is not in either of the rejected states mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, - mCameraId, - &rejectSourceStates); + String8::format("%d", mCameraId), rejected); + // Notify flashlight that a camera device is closed. + mCameraService->mFlashlight->deviceClosed( + String8::format("%d", mCameraId)); } // Always stop watching, even if no camera op is active if (mOpsCallback != NULL) { @@ -1518,26 +1760,15 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa // ---------------------------------------------------------------------------- -Mutex* CameraService::Client::getClientLockFromCookie(void* user) { - return gCameraService->getClientLockById((int)(intptr_t) user); -} - -// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should -// be acquired for this to be safe -CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { - BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user); - // OK: only CameraClient calls this, and they already cast anyway. - Client* client = static_cast<Client*>(basicClient); - - // This could happen if the Client is in the process of shutting down (the - // last strong reference is gone, but the destructor hasn't finished - // stopping the hardware). - if (client == NULL) return NULL; - - // destruction already started, so should not be accessed - if (client->mDestructionStarted) return NULL; - - return client; +// Provide client strong pointer for callbacks. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) { + String8 cameraId = String8::format("%d", (int)(intptr_t) user); + auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId); + if (clientDescriptor != nullptr) { + return sp<Client>{ + static_cast<Client*>(clientDescriptor->getValue().get())}; + } + return sp<Client>{nullptr}; } void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, @@ -1549,7 +1780,6 @@ void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode void CameraService::Client::disconnect() { ALOGV("Client::disconnect"); BasicClient::disconnect(); - mCameraService->setCameraFree(mCameraId); } CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client): @@ -1565,30 +1795,101 @@ void CameraService::Client::OpsCallback::opChanged(int32_t op, } // ---------------------------------------------------------------------------- -// IProCamera +// CameraState // ---------------------------------------------------------------------------- -CameraService::ProClient::ProClient(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid) - : CameraService::BasicClient(cameraService, IInterface::asBinder(remoteCallback), - clientPackageName, cameraId, cameraFacing, - clientPid, clientUid, servicePid) -{ - mRemoteCallback = remoteCallback; +CameraService::CameraState::CameraState(const String8& id, int cost, + const std::set<String8>& conflicting) : mId(id), + mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {} + +CameraService::CameraState::~CameraState() {} + +ICameraServiceListener::Status CameraService::CameraState::getStatus() const { + Mutex::Autolock lock(mStatusLock); + return mStatus; } -CameraService::ProClient::~ProClient() { +CameraParameters CameraService::CameraState::getShimParams() const { + return mShimParams; } -void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras) { - mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); +void CameraService::CameraState::setShimParams(const CameraParameters& params) { + mShimParams = params; +} + +int CameraService::CameraState::getCost() const { + return mCost; +} + +std::set<String8> CameraService::CameraState::getConflicting() const { + return mConflicting; +} + +String8 CameraService::CameraState::getId() const { + return mId; +} + +// ---------------------------------------------------------------------------- +// CameraClientManager +// ---------------------------------------------------------------------------- + +CameraService::CameraClientManager::~CameraClientManager() {} + +sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient( + const String8& id) const { + auto descriptor = get(id); + if (descriptor == nullptr) { + return sp<BasicClient>{nullptr}; + } + return descriptor->getValue(); +} + +String8 CameraService::CameraClientManager::toString() const { + auto all = getAll(); + String8 ret("["); + bool hasAny = false; + for (auto& i : all) { + hasAny = true; + String8 key = i->getKey(); + int32_t cost = i->getCost(); + int32_t pid = i->getOwnerId(); + int32_t priority = i->getPriority(); + auto conflicting = i->getConflicting(); + auto clientSp = i->getValue(); + String8 packageName; + if (clientSp.get() != nullptr) { + packageName = String8{clientSp->getPackageName()}; + } + ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %" + PRId32 ", ", key.string(), cost, pid, priority); + + if (packageName.size() != 0) { + ret.appendFormat("Client Package Name: %s", packageName.string()); + } + + ret.append(", Conflicting Client Devices: {"); + for (auto& j : conflicting) { + ret.appendFormat("%s, ", j.string()); + } + ret.append("})"); + } + if (hasAny) ret.append("\n"); + ret.append("]\n"); + return ret; +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const String8& key, const sp<BasicClient>& value, int32_t cost, + const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) { + + return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>( + key, value, cost, conflictingKeys, priority, ownerId); +} + +CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor( + const sp<BasicClient>& value, const CameraService::DescriptorPtr& partial) { + return makeClientDescriptor(partial->getKey(), value, partial->getCost(), + partial->getConflicting(), partial->getPriority(), partial->getOwnerId()); } // ---------------------------------------------------------------------------- @@ -1633,15 +1934,13 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { return NO_ERROR; } - result = String8::format("Camera module HAL API version: 0x%x\n", - mModule->common.hal_api_version); - result.appendFormat("Camera module API version: 0x%x\n", - mModule->common.module_api_version); - result.appendFormat("Camera module name: %s\n", - mModule->common.name); - result.appendFormat("Camera module author: %s\n", - mModule->common.author); - result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + result = String8::format("Camera module HAL API version: 0x%x\n", mModule->getHalApiVersion()); + result.appendFormat("Camera module API version: 0x%x\n", mModule->getModuleApiVersion()); + result.appendFormat("Camera module name: %s\n", mModule->getModuleName()); + result.appendFormat("Camera module author: %s\n", mModule->getModuleAuthor()); + result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras); + String8 activeClientString = mActiveClientManager.toString(); + result.appendFormat("Active Camera Clients:\n%s", activeClientString.string()); sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); if (desc == NULL) { @@ -1656,11 +1955,31 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { desc->dump(fd, /*verbosity*/2, /*indentation*/4); } - for (int i = 0; i < mNumberOfCameras; i++) { - result = String8::format("Camera %d static information:\n", i); + result = String8("Prior client events (most recent at top):\n"); + + for (const auto& msg : mEventLog) { + result.appendFormat("%s\n", msg.string()); + } + + if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) { + result.append("...\n"); + } + + write(fd, result.string(), result.size()); + + bool stateLocked = tryLock(mCameraStatesLock); + if (!stateLocked) { + result = String8::format("CameraStates in use, may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + for (auto& state : mCameraStates) { + String8 cameraId = state.first; + result = String8::format("Camera %s information:\n", cameraId.string()); camera_info info; - status_t rc = mModule->get_camera_info(i, &info); + // TODO: Change getCameraInfo + HAL to use String cameraIds + status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info); if (rc != OK) { result.appendFormat(" Error reading static information!\n"); write(fd, result.string(), result.size()); @@ -1669,13 +1988,24 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT"); result.appendFormat(" Orientation: %d\n", info.orientation); int deviceVersion; - if (mModule->common.module_api_version < - CAMERA_MODULE_API_VERSION_2_0) { + if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0) { deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; } else { deviceVersion = info.device_version; } - result.appendFormat(" Device version: 0x%x\n", deviceVersion); + + auto conflicting = state.second->getConflicting(); + result.appendFormat(" Resource Cost: %d\n", state.second->getCost()); + result.appendFormat(" Conflicting Devices:"); + for (auto& id : conflicting) { + result.appendFormat(" %s", cameraId.string()); + } + if (conflicting.size() == 0) { + result.appendFormat(" NONE"); + } + result.appendFormat("\n"); + + result.appendFormat(" Device version: %#x\n", deviceVersion); if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { result.appendFormat(" Device static metadata:\n"); write(fd, result.string(), result.size()); @@ -1684,19 +2014,38 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { } else { write(fd, result.string(), result.size()); } + + CameraParameters p = state.second->getShimParams(); + if (!p.isEmpty()) { + result = String8::format(" Camera1 API shim is using parameters:\n "); + write(fd, result.string(), result.size()); + p.dump(fd, args); + } } - sp<BasicClient> client = mClient[i].promote(); - if (client == 0) { - result = String8::format(" Device is closed, no client instance\n"); + auto clientDescriptor = mActiveClientManager.get(cameraId); + if (clientDescriptor == nullptr) { + result = String8::format(" Device %s is closed, no client instance\n", + cameraId.string()); write(fd, result.string(), result.size()); continue; } hasClient = true; - result = String8::format(" Device is open. Client instance dump:\n"); + result = String8::format(" Device %s is open. Client instance dump:\n\n", + cameraId.string()); + result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority()); + result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId()); + + auto client = clientDescriptor->getValue(); + result.appendFormat("Client package: %s\n", + String8(client->getPackageName()).string()); write(fd, result.string(), result.size()); + client->dump(fd, args); } + + if (stateLocked) mCameraStatesLock.unlock(); + if (!hasClient) { result = String8::format("\nNo active camera clients yet.\n"); write(fd, result.string(), result.size()); @@ -1720,112 +2069,119 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { write(fd, result.string(), result.size()); } } - } return NO_ERROR; } -/*virtual*/void CameraService::binderDied( - const wp<IBinder> &who) { +void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) { + Mutex::Autolock al(mTorchClientMapMutex); + for (size_t i = 0; i < mTorchClientMap.size(); i++) { + if (mTorchClientMap[i] == who) { + // turn off the torch mode that was turned on by dead client + String8 cameraId = mTorchClientMap.keyAt(i); + status_t res = mFlashlight->setTorchMode(cameraId, false); + if (res) { + ALOGE("%s: torch client died but couldn't turn off torch: " + "%s (%d)", __FUNCTION__, strerror(-res), res); + return; + } + mTorchClientMap.removeItemsAt(i); + break; + } + } +} + +/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) { /** * While tempting to promote the wp<IBinder> into a sp, * it's actually not supported by the binder driver */ - ALOGV("java clients' binder died"); + // check torch client + handleTorchClientBinderDied(who); - sp<BasicClient> cameraClient = getClientByRemote(who); - - if (cameraClient == 0) { - ALOGV("java clients' binder death already cleaned up (normal case)"); + // check camera device client + if(!evictClientIdByRemote(who)) { + ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__); return; } - ALOGW("Disconnecting camera client %p since the binder for it " - "died (this pid %d)", cameraClient.get(), getCallingPid()); - - cameraClient->disconnect(); - + ALOGE("%s: Java client's binder died, removing it from the list of active clients", + __FUNCTION__); } -void CameraService::updateStatus(ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates) { - // do not lock mServiceLock here or can get into a deadlock from - // connect() -> ProClient::disconnect -> updateStatus - Mutex::Autolock lock(mStatusMutex); - - ICameraServiceListener::Status oldStatus = mStatusList[cameraId]; - - mStatusList[cameraId] = status; +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) { + updateStatus(status, cameraId, {}); +} - if (oldStatus != status) { - ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x", - __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status); +void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates) { + // Do not lock mServiceLock here or can get into a deadlock from + // connect() -> disconnect -> updateStatus - if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && - (status != ICameraServiceListener::STATUS_PRESENT && - status != ICameraServiceListener::STATUS_ENUMERATING)) { + auto state = getCameraState(cameraId); - ALOGW("%s: From NOT_PRESENT can only transition into PRESENT" - " or ENUMERATING", __FUNCTION__); - mStatusList[cameraId] = oldStatus; - return; - } + if (state == nullptr) { + ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__, + cameraId.string()); + return; + } - if (rejectSourceStates != NULL) { - const StatusVector &rejectList = *rejectSourceStates; - StatusVector::const_iterator it = rejectList.begin(); - - /** - * Sometimes we want to conditionally do a transition. - * For example if a client disconnects, we want to go to PRESENT - * only if we weren't already in NOT_PRESENT or ENUMERATING. - */ - for (; it != rejectList.end(); ++it) { - if (oldStatus == *it) { - ALOGV("%s: Rejecting status transition for Camera ID %d, " - " since the source state was was in one of the bad " - " states.", __FUNCTION__, cameraId); - mStatusList[cameraId] = oldStatus; - return; - } + // Update the status for this camera state, then send the onStatusChangedCallbacks to each + // of the listeners with both the mStatusStatus and mStatusListenerLock held + state->updateStatus(status, cameraId, rejectSourceStates, [this] + (const String8& cameraId, ICameraServiceListener::Status status) { + + // Update torch status + if (status == ICameraServiceListener::STATUS_NOT_PRESENT || + status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { + // Update torch status to not available when the camera device becomes not present + // or not available. + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE); + } else if (status == ICameraServiceListener::STATUS_PRESENT) { + // Update torch status to available when the camera device becomes present or + // available + onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF); } - } - /** - * ProClients lose their exclusive lock. - * - Done before the CameraClient can initialize the HAL device, - * since we want to be able to close it before they get to initialize - */ - if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) { - Vector<wp<ProClient> > proClients(mProClientList[cameraId]); - Vector<wp<ProClient> >::const_iterator it; - - for (it = proClients.begin(); it != proClients.end(); ++it) { - sp<ProClient> proCl = it->promote(); - if (proCl.get() != NULL) { - proCl->onExclusiveLockStolen(); - } + Mutex::Autolock lock(mStatusListenerLock); + + for (auto& listener : mListenerList) { + // TODO: Refactor status listeners to use strings for Camera IDs and remove this. + int id = cameraIdToInt(cameraId); + if (id != -1) listener->onStatusChanged(status, id); } - } + }); +} - Vector<sp<ICameraServiceListener> >::const_iterator it; - for (it = mListenerList.begin(); it != mListenerList.end(); ++it) { - (*it)->onStatusChanged(status, cameraId); - } +status_t CameraService::getTorchStatusLocked( + const String8& cameraId, + ICameraServiceListener::TorchStatus *status) const { + if (!status) { + return BAD_VALUE; + } + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + // invalid camera ID or the camera doesn't have a flash unit + return NAME_NOT_FOUND; } + + *status = mTorchStatusMap.valueAt(index); + return OK; } -ICameraServiceListener::Status CameraService::getStatus(int cameraId) const { - if (cameraId < 0 || cameraId >= MAX_CAMERAS) { - ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); - return ICameraServiceListener::STATUS_UNKNOWN; +status_t CameraService::setTorchStatusLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus status) { + ssize_t index = mTorchStatusMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + return BAD_VALUE; } + ICameraServiceListener::TorchStatus& item = + mTorchStatusMap.editValueAt(index); + item = status; - Mutex::Autolock al(mStatusMutex); - return mStatusList[cameraId]; + return OK; } }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 126d8d9..ca1c504 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H +#include <cutils/multiuser.h> #include <utils/Vector.h> #include <utils/KeyedVector.h> #include <binder/AppOpsManager.h> @@ -27,8 +28,6 @@ #include <camera/ICamera.h> #include <camera/ICameraClient.h> -#include <camera/IProCameraUser.h> -#include <camera/IProCameraCallbacks.h> #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> #include <camera/VendorTagDescriptor.h> @@ -36,9 +35,17 @@ #include <camera/CameraParameters.h> #include <camera/ICameraServiceListener.h> +#include "CameraFlashlight.h" -/* This needs to be increased if we can have more cameras */ -#define MAX_CAMERAS 2 +#include "common/CameraModule.h" +#include "utils/AutoConditionLock.h" +#include "utils/ClientManager.h" +#include "utils/RingBuffer.h" + +#include <set> +#include <string> +#include <map> +#include <memory> namespace android { @@ -58,6 +65,39 @@ public: class Client; class BasicClient; + enum apiLevel { + API_1 = 1, + API_2 = 2 + }; + + // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java) + static const int PROCESS_STATE_NONEXISTENT = -1; + static const int PROCESS_STATE_PERSISTENT = 0; + static const int PROCESS_STATE_PERSISTENT_UI = 1; + static const int PROCESS_STATE_TOP = 2; + static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; + static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; + static const int PROCESS_STATE_BACKUP = 5; + static const int PROCESS_STATE_HEAVY_WEIGHT = 6; + static const int PROCESS_STATE_SERVICE = 7; + static const int PROCESS_STATE_RECEIVER = 8; + static const int PROCESS_STATE_HOME = 9; + static const int PROCESS_STATE_LAST_ACTIVITY = 10; + static const int PROCESS_STATE_CACHED_ACTIVITY = 11; + static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; + static const int PROCESS_STATE_CACHED_EMPTY = 13; + + // 3 second busy timeout when other clients are connecting + static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000; + + // Default number of messages to store in eviction log + static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50; + + enum { + // Default last user id + DEFAULT_LAST_USER_ID = 0, + }; + // Implementation of BinderService<T> static char const* getServiceName() { return "media.camera"; } @@ -66,8 +106,11 @@ public: ///////////////////////////////////////////////////////////////////// // HAL Callbacks - virtual void onDeviceStatusChanged(int cameraId, - int newStatus); + virtual void onDeviceStatusChanged(camera_device_status_t cameraId, + camera_device_status_t newStatus); + virtual void onTorchStatusChanged(const String8& cameraId, + ICameraServiceListener::TorchStatus + newStatus); ///////////////////////////////////////////////////////////////////// // ICameraService @@ -88,11 +131,6 @@ public: /*out*/ sp<ICamera>& device); - virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, - int cameraId, const String16& clientPackageName, int clientUid, - /*out*/ - sp<IProCameraUser>& device); - virtual status_t connectDevice( const sp<ICameraDeviceCallbacks>& cameraCb, int cameraId, @@ -110,6 +148,11 @@ public: /*out*/ String16* parameters); + virtual status_t setTorchMode(const String16& cameraId, bool enabled, + const sp<IBinder>& clientBinder); + + virtual void notifySystemEvent(int eventId, int arg0); + // OK = supports api of that version, -EOPNOTSUPP = does not support virtual status_t supportsCameraApi( int cameraId, int apiVersion); @@ -122,7 +165,6 @@ public: ///////////////////////////////////////////////////////////////////// // Client functionality - virtual void removeClientByRemote(const wp<IBinder>& remoteBinder); enum sound_kind { SOUND_SHUTTER = 0, @@ -140,33 +182,36 @@ public: ///////////////////////////////////////////////////////////////////// // Shared utilities - static status_t filterOpenErrorCode(status_t err); static status_t filterGetInfoErrorCode(status_t err); ///////////////////////////////////////////////////////////////////// // CameraClient functionality - // returns plain pointer of client. Note that mClientLock should be acquired to - // prevent the client from destruction. The result can be NULL. - virtual BasicClient* getClientByIdUnsafe(int cameraId); - virtual Mutex* getClientLockById(int cameraId); - class BasicClient : public virtual RefBase { public: - virtual status_t initialize(camera_module_t *module) = 0; + virtual status_t initialize(CameraModule *module) = 0; virtual void disconnect(); // because we can't virtually inherit IInterface, which breaks // virtual inheritance virtual sp<IBinder> asBinderWrapper() = 0; - // Return the remote callback binder object (e.g. IProCameraCallbacks) + // Return the remote callback binder object (e.g. ICameraDeviceCallbacks) sp<IBinder> getRemote() { return mRemoteBinder; } virtual status_t dump(int fd, const Vector<String16>& args) = 0; + // Return the package name for this client + virtual String16 getPackageName() const; + + // Notify client about a fatal error + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) = 0; + + // Get the PID of the application client using this + virtual int getClientPid() const; protected: BasicClient(const sp<CameraService>& cameraService, const sp<IBinder>& remoteCallback, @@ -193,6 +238,7 @@ public: pid_t mClientPid; uid_t mClientUid; // immutable after constructor pid_t mServicePid; // immutable after constructor + bool mDisconnected; // - The app-side Binder interface to receive callbacks from us sp<IBinder> mRemoteBinder; // immutable after constructor @@ -201,10 +247,6 @@ public: status_t startCameraOps(); status_t finishCameraOps(); - // Notify client about a fatal error - virtual void notifyError( - ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras) = 0; private: AppOpsManager mAppOpsManager; @@ -276,13 +318,11 @@ public: return asBinder(this); } - protected: - static Mutex* getClientLockFromCookie(void* user); - // convert client from cookie. Client lock should be acquired before getting Client. - static Client* getClientFromCookie(void* user); - virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, const CaptureResultExtras& resultExtras); + protected: + // Convert client from cookie. + static sp<CameraService::Client> getClientFromCookie(void* user); // Initialized in constructor @@ -291,92 +331,231 @@ public: }; // class Client - class ProClient : public BnProCameraUser, public BasicClient { + typedef std::shared_ptr<resource_policy::ClientDescriptor<String8, + sp<CameraService::BasicClient>>> DescriptorPtr; + + /** + * A container class for managing active camera clients that are using HAL devices. Active + * clients are represented by ClientDescriptor objects that contain strong pointers to the + * actual BasicClient subclass binder interface implementation. + * + * This class manages the eviction behavior for the camera clients. See the parent class + * implementation in utils/ClientManager for the specifics of this behavior. + */ + class CameraClientManager : + public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> { public: - typedef IProCameraCallbacks TCamCallbacks; + virtual ~CameraClientManager(); + + /** + * Return a strong pointer to the active BasicClient for this camera ID, or an empty + * if none exists. + */ + sp<CameraService::BasicClient> getCameraClient(const String8& id) const; + + /** + * Return a string describing the current state. + */ + String8 toString() const; + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer. + */ + static DescriptorPtr makeClientDescriptor(const String8& key, const sp<BasicClient>& value, + int32_t cost, const std::set<String8>& conflictingKeys, int32_t priority, + int32_t ownerId); + + /** + * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with + * values intialized from a prior ClientDescriptor. + */ + static DescriptorPtr makeClientDescriptor(const sp<BasicClient>& value, + const CameraService::DescriptorPtr& partial); + + }; // class CameraClientManager - ProClient(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid); +private: - virtual ~ProClient(); + /** + * Container class for the state of each logical camera device, including: ID, status, and + * dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates + * represents the camera devices advertised by the HAL (and any USB devices, when we add + * those). + * + * This container does NOT represent an active camera client. These are represented using + * the ClientDescriptors stored in mActiveClientManager. + */ + class CameraState { + public: + /** + * Make a new CameraState and set the ID, cost, and conflicting devices using the values + * returned in the HAL's camera_info struct for each device. + */ + CameraState(const String8& id, int cost, const std::set<String8>& conflicting); + virtual ~CameraState(); + + /** + * Return the status for this device. + * + * This method acquires mStatusLock. + */ + ICameraServiceListener::Status getStatus() const; + + /** + * This function updates the status for this camera device, unless the given status + * is in the given list of rejected status states, and execute the function passed in + * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status) + * if the status has changed. + * + * This method is idempotent, and will not result in the function passed to + * onStatusUpdateLocked being called more than once for the same arguments. + * This method aquires mStatusLock. + */ + template<class Func> + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, + Func onStatusUpdatedLocked); + + /** + * Return the last set CameraParameters object generated from the information returned by + * the HAL for this device (or an empty CameraParameters object if none has been set). + */ + CameraParameters getShimParams() const; + + /** + * Set the CameraParameters for this device. + */ + void setShimParams(const CameraParameters& params); + + /** + * Return the resource_cost advertised by the HAL for this device. + */ + int getCost() const; + + /** + * Return a set of the IDs of conflicting devices advertised by the HAL for this device. + */ + std::set<String8> getConflicting() const; + + /** + * Return the ID of this camera device. + */ + String8 getId() const; - const sp<IProCameraCallbacks>& getRemoteCallback() { - return mRemoteCallback; - } + private: + const String8 mId; + ICameraServiceListener::Status mStatus; // protected by mStatusLock + const int mCost; + std::set<String8> mConflicting; + mutable Mutex mStatusLock; + CameraParameters mShimParams; + }; // class CameraState - /*** - IProCamera implementation - ***/ - virtual status_t connect(const sp<IProCameraCallbacks>& callbacks) - = 0; - virtual status_t exclusiveTryLock() = 0; - virtual status_t exclusiveLock() = 0; - virtual status_t exclusiveUnlock() = 0; + // Delay-load the Camera HAL module + virtual void onFirstRef(); - virtual bool hasExclusiveLock() = 0; + // Check if we can connect, before we acquire the service lock. + status_t validateConnectLocked(const String8& cameraId, /*inout*/int& clientUid) const; - // Note that the callee gets a copy of the metadata. - virtual int submitRequest(camera_metadata_t* metadata, - bool streaming = false) = 0; - virtual status_t cancelRequest(int requestId) = 0; + // Handle active client evictions, and update service state. + // Only call with with mServiceLock held. + status_t handleEvictionsLocked(const String8& cameraId, int clientPid, + apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName, + /*out*/ + sp<BasicClient>* client, + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial); - // Callbacks from camera service - virtual void onExclusiveLockStolen() = 0; + // Single implementation shared between the various connect calls + template<class CALLBACK, class CLIENT> + status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion, + const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel, + bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device); - protected: - virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, - const CaptureResultExtras& resultExtras); - sp<IProCameraCallbacks> mRemoteCallback; - }; // class ProClient + // Lock guarding camera service state + Mutex mServiceLock; -private: + // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients + std::shared_ptr<WaitableMutexWrapper> mServiceLockWrapper; - // Delay-load the Camera HAL module - virtual void onFirstRef(); + // Return NO_ERROR if the device with a give ID can be connected to + status_t checkIfDeviceIsUsable(const String8& cameraId) const; - // Step 1. Check if we can connect, before we acquire the service lock. - status_t validateConnect(int cameraId, - /*inout*/ - int& clientUid) const; + // Container for managing currently active application-layer clients + CameraClientManager mActiveClientManager; - // Step 2. Check if we can connect, after we acquire the service lock. - bool canConnectUnsafe(int cameraId, - const String16& clientPackageName, - const sp<IBinder>& remoteCallback, - /*out*/ - sp<BasicClient> &client); + // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock + std::map<String8, std::shared_ptr<CameraState>> mCameraStates; - // When connection is successful, initialize client and track its death - status_t connectFinishUnsafe(const sp<BasicClient>& client, - const sp<IBinder>& remoteCallback); + // Mutex guarding mCameraStates map + mutable Mutex mCameraStatesLock; - virtual sp<BasicClient> getClientByRemote(const wp<IBinder>& cameraClient); + // Circular buffer for storing event logging for dumps + RingBuffer<String8> mEventLog; - Mutex mServiceLock; - // either a Client or CameraDeviceClient - wp<BasicClient> mClient[MAX_CAMERAS]; // protected by mServiceLock - Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks - int mNumberOfCameras; + // UID of last user. + int mLastUserId; + + /** + * Get the camera state for a given camera id. + * + * This acquires mCameraStatesLock. + */ + std::shared_ptr<CameraService::CameraState> getCameraState(const String8& cameraId) const; + + /** + * Evict client who's remote binder has died. Returns true if this client was in the active + * list and was disconnected. + * + * This method acquires mServiceLock. + */ + bool evictClientIdByRemote(const wp<IBinder>& cameraClient); - typedef wp<ProClient> weak_pro_client_ptr; - Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS]; + /** + * Remove the given client from the active clients list; does not disconnect the client. + * + * This method acquires mServiceLock. + */ + void removeByClient(const BasicClient* client); - // needs to be called with mServiceLock held - sp<BasicClient> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex); - sp<ProClient> findProClientUnsafe( - const wp<IBinder>& cameraCallbacksRemote); + /** + * Add new client to active clients list after conflicting clients have disconnected using the + * values set in the partial descriptor passed in to construct the actual client descriptor. + * This is typically called at the end of a connect call. + * + * This method must be called with mServiceLock held. + */ + void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc); - // atomics to record whether the hardware is allocated to some client. - volatile int32_t mBusy[MAX_CAMERAS]; - void setCameraBusy(int cameraId); - void setCameraFree(int cameraId); + /** + * Returns the integer corresponding to the given camera ID string, or -1 on failure. + */ + static int cameraIdToInt(const String8& cameraId); + + /** + * Remove a single client corresponding to the given camera id from the list of active clients. + * If none exists, return an empty strongpointer. + * + * This method must be called with mServiceLock held. + */ + sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId); + + /** + * Handle a notification that the current device user has changed. + */ + void doUserSwitch(int newUserId); + + /** + * Add a event log message that a client has been disconnected. + */ + void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + /** + * Add a event log message that a client has been connected. + */ + void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage); + + int mNumberOfCameras; // sounds MediaPlayer* newMediaPlayer(const char *file); @@ -385,45 +564,60 @@ private: sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; int mSoundRef; // reference count (release all MediaPlayer when 0) - camera_module_t *mModule; - - Vector<sp<ICameraServiceListener> > - mListenerList; + CameraModule* mModule; - // guard only mStatusList and the broadcasting of ICameraServiceListener - mutable Mutex mStatusMutex; - ICameraServiceListener::Status - mStatusList[MAX_CAMERAS]; + // Guarded by mStatusListenerMutex + std::vector<sp<ICameraServiceListener>> mListenerList; + Mutex mStatusListenerLock; - // Read the current status (locks mStatusMutex) - ICameraServiceListener::Status - getStatus(int cameraId) const; - - typedef Vector<ICameraServiceListener::Status> StatusVector; - // Broadcast the new status if it changed (locks the service mutex) - void updateStatus( - ICameraServiceListener::Status status, - int32_t cameraId, - const StatusVector *rejectSourceStates = NULL); + /** + * Update the status for the given camera id (if that device exists), and broadcast the + * status update to all current ICameraServiceListeners if the status has changed. Any + * statuses in rejectedSourceStates will be ignored. + * + * This method must be idempotent. + * This method acquires mStatusLock and mStatusListenerLock. + */ + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectedSourceStates); + void updateStatus(ICameraServiceListener::Status status, const String8& cameraId); + + // flashlight control + sp<CameraFlashlight> mFlashlight; + // guard mTorchStatusMap + Mutex mTorchStatusMutex; + // guard mTorchClientMap + Mutex mTorchClientMapMutex; + // camera id -> torch status + KeyedVector<String8, ICameraServiceListener::TorchStatus> mTorchStatusMap; + // camera id -> torch client binder + // only store the last client that turns on each camera's torch mode + KeyedVector<String8, sp<IBinder> > mTorchClientMap; + + // check and handle if torch client's process has died + void handleTorchClientBinderDied(const wp<IBinder> &who); + + // handle torch mode status change and invoke callbacks. mTorchStatusMutex + // should be locked. + void onTorchStatusChangedLocked(const String8& cameraId, + ICameraServiceListener::TorchStatus newStatus); + + // get a camera's torch status. mTorchStatusMutex should be locked. + status_t getTorchStatusLocked(const String8 &cameraId, + ICameraServiceListener::TorchStatus *status) const; + + // set a camera's torch status. mTorchStatusMutex should be locked. + status_t setTorchStatusLocked(const String8 &cameraId, + ICameraServiceListener::TorchStatus status); // IBinder::DeathRecipient implementation virtual void binderDied(const wp<IBinder> &who); // Helpers - bool isValidCameraId(int cameraId); - bool setUpVendorTags(); /** - * A mapping of camera ids to CameraParameters returned by that camera device. - * - * This cache is used to generate CameraCharacteristic metadata when using - * the HAL1 shim. - */ - KeyedVector<int, CameraParameters> mShimParams; - - /** * Initialize and cache the metadata used by the HAL1 shim for a given cameraId. * * Returns OK on success, or a negative error code. @@ -446,25 +640,192 @@ private: */ status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo); + static int getCallingPid(); + + static int getCallingUid(); + /** - * Connect a new camera client. This should only be used while holding the - * mutex for mServiceLock. - * - * Returns OK on success, or a negative error code. + * Get the current system time as a formatted string. */ - status_t connectHelperLocked( - /*out*/ - sp<Client>& client, - /*in*/ - const sp<ICameraClient>& cameraClient, - int cameraId, - const String16& clientPackageName, - int clientUid, - int callingPid, - int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED, - bool legacyMode = false); + static String8 getFormattedCurrentTime(); + + /** + * Get the camera eviction priority from the current process state given by ActivityManager. + */ + static int getCameraPriorityFromProcState(int procState); + + static status_t makeClient(const sp<CameraService>& cameraService, + const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId, + int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode, + int halVersion, int deviceVersion, apiLevel effectiveApiLevel, + /*out*/sp<BasicClient>* client); }; +template<class Func> +void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status, + const String8& cameraId, + std::initializer_list<ICameraServiceListener::Status> rejectSourceStates, + Func onStatusUpdatedLocked) { + Mutex::Autolock lock(mStatusLock); + ICameraServiceListener::Status oldStatus = mStatus; + mStatus = status; + + if (oldStatus == status) { + return; + } + + ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__, + cameraId.string(), oldStatus, status); + + if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT && + (status != ICameraServiceListener::STATUS_PRESENT && + status != ICameraServiceListener::STATUS_ENUMERATING)) { + + ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING", + __FUNCTION__); + mStatus = oldStatus; + return; + } + + /** + * Sometimes we want to conditionally do a transition. + * For example if a client disconnects, we want to go to PRESENT + * only if we weren't already in NOT_PRESENT or ENUMERATING. + */ + for (auto& rejectStatus : rejectSourceStates) { + if (oldStatus == rejectStatus) { + ALOGV("%s: Rejecting status transition for Camera ID %s, since the source " + "state was was in one of the bad states.", __FUNCTION__, cameraId.string()); + mStatus = oldStatus; + return; + } + } + + onStatusUpdatedLocked(cameraId, status); +} + + +template<class CALLBACK, class CLIENT> +status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, + int halVersion, const String16& clientPackageName, int clientUid, + apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly, + /*out*/sp<CLIENT>& device) { + status_t ret = NO_ERROR; + String8 clientName8(clientPackageName); + int clientPid = getCallingPid(); + + ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and " + "Camera API version %d", clientPid, clientName8.string(), cameraId.string(), + halVersion, static_cast<int>(effectiveApiLevel)); + + sp<CLIENT> client = nullptr; + { + // Acquire mServiceLock and prevent other clients from connecting + std::unique_ptr<AutoConditionLock> lock = + AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS); + + if (lock == nullptr) { + ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)." + , clientPid); + return -EBUSY; + } + + // Enforce client permissions and do basic sanity checks + if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) { + return ret; + } + mLastUserId = multiuser_get_user_id(clientUid); + + // Check the shim parameters after acquiring lock, if they have already been updated and + // we were doing a shim update, return immediately + if (shimUpdateOnly) { + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + if (!cameraState->getShimParams().isEmpty()) return NO_ERROR; + } + } + + sp<BasicClient> clientTmp = nullptr; + std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial; + if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel, + IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp, + /*out*/&partial)) != NO_ERROR) { + return ret; + } + + if (clientTmp.get() != nullptr) { + // Handle special case for API1 MediaRecorder where the existing client is returned + device = static_cast<CLIENT*>(clientTmp.get()); + return NO_ERROR; + } + + // give flashlight a chance to close devices if necessary. + mFlashlight->prepareDeviceOpen(cameraId); + + // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs + int id = cameraIdToInt(cameraId); + if (id == -1) { + ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__, + cameraId.string()); + return BAD_VALUE; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(id, /*out*/&facing); + sp<BasicClient> tmp = nullptr; + if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid, + clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel, + /*out*/&tmp)) != NO_ERROR) { + return ret; + } + client = static_cast<CLIENT*>(tmp.get()); + + LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state", + __FUNCTION__); + + if ((ret = client->initialize(mModule)) != OK) { + ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__); + return ret; + } + + sp<IBinder> remoteCallback = client->getRemote(); + if (remoteCallback != nullptr) { + remoteCallback->linkToDeath(this); + } + + // Update shim paremeters for legacy clients + if (effectiveApiLevel == API_1) { + // Assume we have always received a Client subclass for API1 + sp<Client> shimClient = reinterpret_cast<Client*>(client.get()); + String8 rawParams = shimClient->getParameters(); + CameraParameters params(rawParams); + + auto cameraState = getCameraState(cameraId); + if (cameraState != nullptr) { + cameraState->setShimParams(params); + } else { + ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.", + __FUNCTION__, cameraId.string()); + } + } + + if (shimUpdateOnly) { + // If only updating legacy shim parameters, immediately disconnect client + mServiceLock.unlock(); + client->disconnect(); + mServiceLock.lock(); + } else { + // Otherwise, add client to active clients list + finishConnectLocked(client, partial); + } + } // lock is destroyed, allow further connect calls + + // Important: release the mutex here so the client can call back into the service from its + // destructor (can be at the end of the call) + device = client; + return NO_ERROR; +} + } // namespace android #endif diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index 0ed5586..6f44aee 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -67,7 +67,7 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, mLegacyMode = legacyMode; } -status_t Camera2Client::initialize(camera_module_t *module) +status_t Camera2Client::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId); @@ -163,11 +163,9 @@ Camera2Client::~Camera2Client() { status_t Camera2Client::dump(int fd, const Vector<String16>& args) { String8 result; - result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n", - mCameraId, + result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId, (getRemoteCallback() != NULL ? (IInterface::asBinder(getRemoteCallback()).get()) : NULL), - String8(mClientPackageName).string(), mClientPid); result.append(" State: "); #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; @@ -1959,7 +1957,7 @@ size_t Camera2Client::calculateBufferSize(int width, int height, return width * height * 2; case HAL_PIXEL_FORMAT_RGBA_8888: return width * height * 4; - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: return width * height * 2; default: ALOGE("%s: Unknown preview format: %x", diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h index d68bb29..5a8241f 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.h +++ b/services/camera/libcameraservice/api1/Camera2Client.h @@ -94,7 +94,7 @@ public: virtual ~Camera2Client(); - status_t initialize(camera_module_t *module); + status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index bbb2fe0..e552633 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -59,7 +59,7 @@ CameraClient::CameraClient(const sp<CameraService>& cameraService, LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); } -status_t CameraClient::initialize(camera_module_t *module) { +status_t CameraClient::initialize(CameraModule *module) { int callingPid = getCallingPid(); status_t res; @@ -75,7 +75,7 @@ status_t CameraClient::initialize(camera_module_t *module) { snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId); mHardware = new CameraHardwareInterface(camera_device_name); - res = mHardware->initialize(&module->common); + res = mHardware->initialize(module); if (res != OK) { ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); @@ -99,12 +99,7 @@ status_t CameraClient::initialize(camera_module_t *module) { // tear down the client CameraClient::~CameraClient() { - // this lock should never be NULL - Mutex* lock = mCameraService->getClientLockById(mCameraId); - lock->lock(); mDestructionStarted = true; - // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect - lock->unlock(); int callingPid = getCallingPid(); LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this); @@ -116,11 +111,11 @@ status_t CameraClient::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; - size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", + size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n", mCameraId, (getRemoteCallback() != NULL ? IInterface::asBinder(getRemoteCallback()).get() : NULL), - mClientPid); + mClientUid); len = (len > SIZE - 1) ? SIZE - 1 : len; write(fd, buffer, len); @@ -677,6 +672,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { LOG1("lockIfMessageWanted(%d): waited for %d ms", msgType, sleepCount * CHECK_MESSAGE_INTERVAL); } + + // If messages are no longer enabled after acquiring lock, release and drop message + if ((mMsgEnabled & msgType) == 0) { + mLock.unlock(); + break; + } + return true; } if (sleepCount++ == 0) { @@ -702,26 +704,13 @@ bool CameraClient::lockIfMessageWanted(int32_t msgType) { // (others) c->dataCallback // dataCallbackTimestamp // (others) c->dataCallbackTimestamp -// -// NOTE: the *Callback functions grab mLock of the client before passing -// control to handle* functions. So the handle* functions must release the -// lock before calling the ICameraClient's callbacks, so those callbacks can -// invoke methods in the Client class again (For example, the preview frame -// callback may want to releaseRecordingFrame). The handle* functions must -// release the lock after all accesses to member variables, so it must be -// handled very carefully. void CameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) { LOG2("notifyCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; @@ -740,13 +729,8 @@ void CameraClient::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { LOG2("dataCallback(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; if (dataPtr == 0 && metadata == NULL) { @@ -778,13 +762,8 @@ void CameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user) { LOG2("dataCallbackTimestamp(%d)", msgType); - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - CameraClient* client = - static_cast<CameraClient*>(getClientFromCookie(user)); - if (client == NULL) return; + sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get()); + if (client.get() == nullptr) return; if (!client->lockIfMessageWanted(msgType)) return; diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h index 63a9d0f..95616b2 100644 --- a/services/camera/libcameraservice/api1/CameraClient.h +++ b/services/camera/libcameraservice/api1/CameraClient.h @@ -68,7 +68,7 @@ public: bool legacyMode = false); ~CameraClient(); - status_t initialize(camera_module_t *module); + status_t initialize(CameraModule *module); status_t dump(int fd, const Vector<String16>& args); diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp index eadaa00..5c8f750 100644 --- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp @@ -154,8 +154,8 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { params.previewWidth, params.previewHeight, callbackFormat, params.previewFormat); res = device->createStream(mCallbackWindow, - params.previewWidth, params.previewHeight, - callbackFormat, &mCallbackStreamId); + params.previewWidth, params.previewHeight, callbackFormat, + HAL_DATASPACE_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for callbacks: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index 2772267..34798bf 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -145,7 +145,8 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { // Create stream for HAL production res = device->createStream(mCaptureWindow, params.pictureWidth, params.pictureHeight, - HAL_PIXEL_FORMAT_BLOB, &mCaptureStreamId); + HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_JFIF, + CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for capture: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 4f4cfb0..6b0f8b5 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -65,15 +65,29 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { const Size MAX_PREVIEW_SIZE = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT }; // Treat the H.264 max size as the max supported video size. MediaProfiles *videoEncoderProfiles = MediaProfiles::getInstance(); - int32_t maxVideoWidth = videoEncoderProfiles->getVideoEncoderParamByName( - "enc.vid.width.max", VIDEO_ENCODER_H264); - int32_t maxVideoHeight = videoEncoderProfiles->getVideoEncoderParamByName( - "enc.vid.height.max", VIDEO_ENCODER_H264); - const Size MAX_VIDEO_SIZE = {maxVideoWidth, maxVideoHeight}; + Vector<video_encoder> encoders = videoEncoderProfiles->getVideoEncoders(); + int32_t maxVideoWidth = 0; + int32_t maxVideoHeight = 0; + for (size_t i = 0; i < encoders.size(); i++) { + int width = videoEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.width.max", encoders[i]); + int height = videoEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.height.max", encoders[i]); + // Treat width/height separately here to handle the case where different + // profile might report max size of different aspect ratio + if (width > maxVideoWidth) { + maxVideoWidth = width; + } + if (height > maxVideoHeight) { + maxVideoHeight = height; + } + } + // This is just an upper bound and may not be an actually valid video size + const Size VIDEO_SIZE_UPPER_BOUND = {maxVideoWidth, maxVideoHeight}; res = getFilteredSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes); if (res != OK) return res; - res = getFilteredSizes(MAX_VIDEO_SIZE, &availableVideoSizes); + res = getFilteredSizes(VIDEO_SIZE_UPPER_BOUND, &availableVideoSizes); if (res != OK) return res; // Select initial preview and video size that's under the initial bound and @@ -182,9 +196,9 @@ status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { supportedPreviewFormats += CameraParameters::PIXEL_FORMAT_YUV420SP; break; - // Not advertizing JPEG, RAW_SENSOR, etc, for preview formats + // Not advertizing JPEG, RAW16, etc, for preview formats case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: case HAL_PIXEL_FORMAT_BLOB: addComma = false; break; @@ -2253,7 +2267,7 @@ const char* Parameters::formatEnumToString(int format) { case HAL_PIXEL_FORMAT_RGBA_8888: // RGBA8888 fmt = CameraParameters::PIXEL_FORMAT_RGBA8888; break; - case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_RAW16: ALOGW("Raw sensor preview format requested."); fmt = CameraParameters::PIXEL_FORMAT_BAYER_RGGB; break; diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp index 470624b..b6071f6 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp @@ -27,6 +27,7 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <gui/BufferItem.h> #include <gui/Surface.h> #include <media/hardware/MetadataBufferType.h> @@ -181,7 +182,8 @@ status_t StreamingProcessor::updatePreviewStream(const Parameters ¶ms) { if (mPreviewStreamId == NO_STREAM) { res = device->createStream(mPreviewWindow, params.previewWidth, params.previewHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mPreviewStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN, + CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId); if (res != OK) { ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)", __FUNCTION__, mId, strerror(-res), res); @@ -420,9 +422,12 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { if (mRecordingStreamId == NO_STREAM) { mRecordingFrameCount = 0; + // Selecting BT.709 colorspace by default + // TODO: Wire this in from encoder side res = device->createStream(mRecordingWindow, params.videoWidth, params.videoHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mRecordingStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_BT709, + CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for recording: " "%s (%d)", __FUNCTION__, mId, @@ -675,7 +680,7 @@ status_t StreamingProcessor::processRecordingFrame() { sp<Camera2Client> client = mClient.promote(); if (client == 0) { // Discard frames during shutdown - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -693,7 +698,7 @@ status_t StreamingProcessor::processRecordingFrame() { with Camera2Client code calling into StreamingProcessor */ SharedParameters::Lock l(client->getParameters()); Mutex::Autolock m(mMutex); - BufferItemConsumer::BufferItem imgBuffer; + BufferItem imgBuffer; res = mRecordingConsumer->acquireBuffer(&imgBuffer, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { @@ -819,8 +824,7 @@ void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) { size_t itemIndex; for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT && item.mGraphicBuffer->handle == imgHandle) { break; @@ -864,8 +868,7 @@ void StreamingProcessor::releaseAllRecordingFramesLocked() { size_t releasedCount = 0; for (size_t itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { - const BufferItemConsumer::BufferItem item = - mRecordingBuffers[itemIndex]; + const BufferItem item = mRecordingBuffers[itemIndex]; if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT) { res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); if (res != OK) { diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h index 1d679a4..2474062 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h @@ -124,7 +124,7 @@ class StreamingProcessor: static const size_t kDefaultRecordingHeapCount = 8; size_t mRecordingHeapCount; - Vector<BufferItemConsumer::BufferItem> mRecordingBuffers; + Vector<BufferItem> mRecordingBuffers; size_t mRecordingHeapHead, mRecordingHeapFree; virtual bool threadLoop(); diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp index 8b7e4b4..a03f9c7 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp @@ -185,8 +185,8 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL : (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; res = device->createStream(mZslWindow, - params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, - streamType, &mZslStreamId); + params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, streamType, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for ZSL: " "%s (%d)", __FUNCTION__, mId, @@ -440,7 +440,7 @@ status_t ZslProcessor::processNewZslBuffer() { zslConsumer = mZslConsumer; } ALOGVV("Trying to get next buffer"); - BufferItemConsumer::BufferItem item; + BufferItem item; res = zslConsumer->acquireBuffer(&item, 0); if (res != OK) { if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h index 2099c38..5f50d7b 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> #include <camera/CaptureResult.h> @@ -103,7 +104,7 @@ class ZslProcessor: sp<ANativeWindow> mZslWindow; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h index fc9f70c..2960478 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h @@ -22,6 +22,7 @@ #include <utils/Vector.h> #include <utils/Mutex.h> #include <utils/Condition.h> +#include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> @@ -104,7 +105,7 @@ class ZslProcessor3 : sp<camera3::Camera3ZslStream> mZslStream; struct ZslPair { - BufferItemConsumer::BufferItem buffer; + BufferItem buffer; CameraMetadata frame; }; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 6a1ee44..8587e0e 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -71,7 +71,7 @@ CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService, ALOGI("CameraDeviceClient %d: Opened", cameraId); } -status_t CameraDeviceClient::initialize(camera_module_t *module) +status_t CameraDeviceClient::initialize(CameraModule *module) { ATRACE_CALL(); status_t res; @@ -314,17 +314,17 @@ status_t CameraDeviceClient::deleteStream(int streamId) { return res; } -status_t CameraDeviceClient::createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer) +status_t CameraDeviceClient::createStream(const OutputConfiguration &outputConfiguration) { ATRACE_CALL(); - ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); + + sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer(); if (bufferProducer == NULL) { ALOGE("%s: bufferProducer must not be null", __FUNCTION__); return BAD_VALUE; @@ -370,7 +370,8 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, sp<IBinder> binder = IInterface::asBinder(bufferProducer); sp<ANativeWindow> anw = new Surface(bufferProducer, useAsync); - // TODO: remove w,h,f since we are ignoring them + int width, height, format; + android_dataspace dataSpace; if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { ALOGE("%s: Camera %d: Failed to query Surface width", __FUNCTION__, @@ -387,6 +388,12 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, mCameraId); return res; } + if ((res = anw->query(anw.get(), NATIVE_WINDOW_DEFAULT_DATASPACE, + reinterpret_cast<int*>(&dataSpace))) != OK) { + ALOGE("%s: Camera %d: Failed to query Surface dataSpace", __FUNCTION__, + mCameraId); + return res; + } // FIXME: remove this override since the default format should be // IMPLEMENTATION_DEFINED. b/9487482 @@ -399,14 +406,17 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, // Round dimensions to the nearest dimensions available for this format if (flexibleConsumer && !CameraDeviceClient::roundBufferDimensionNearest(width, height, - format, mDevice->info(), /*out*/&width, /*out*/&height)) { + format, dataSpace, mDevice->info(), /*out*/&width, /*out*/&height)) { ALOGE("%s: No stream configurations with the format %#x defined, failed to create stream.", __FUNCTION__, format); return BAD_VALUE; } int streamId = -1; - res = mDevice->createStream(anw, width, height, format, &streamId); + res = mDevice->createStream(anw, width, height, format, dataSpace, + static_cast<camera3_stream_rotation_t> + (outputConfiguration.getRotation()), + &streamId); if (res == OK) { mStreamMap.add(binder, streamId); @@ -441,10 +451,12 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height, - int32_t format, const CameraMetadata& info, + int32_t format, android_dataspace dataSpace, const CameraMetadata& info, /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) { camera_metadata_ro_entry streamConfigs = + (dataSpace == HAL_DATASPACE_DEPTH) ? + info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) : info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); int32_t bestWidth = -1; @@ -586,9 +598,7 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) { mCameraId, (getRemoteCallback() != NULL ? IInterface::asBinder(getRemoteCallback()).get() : NULL) ); - result.appendFormat(" Current client: %s (PID %d, UID %u)\n", - String8(mClientPackageName).string(), - mClientPid, mClientUid); + result.appendFormat(" Current client UID %u\n", mClientUid); result.append(" State:\n"); result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter); @@ -635,9 +645,6 @@ void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras, } } -// TODO: refactor the code below this with IProCameraUser. -// it's 100% copy-pasted, so lets not change it right now to make it easier. - void CameraDeviceClient::detachDevice() { if (mDevice == 0) return; diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index 84e46b7..a3dbb90 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -19,6 +19,7 @@ #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> +#include <camera/camera2/OutputConfiguration.h> #include "CameraService.h" #include "common/FrameProcessorBase.h" @@ -83,11 +84,7 @@ public: // Returns -EBUSY if device is not idle virtual status_t deleteStream(int streamId); - virtual status_t createStream( - int width, - int height, - int format, - const sp<IGraphicBufferProducer>& bufferProducer); + virtual status_t createStream(const OutputConfiguration &outputConfiguration); // Create a request object from a template. virtual status_t createDefaultRequest(int templateId, @@ -119,7 +116,7 @@ public: int servicePid); virtual ~CameraDeviceClient(); - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); @@ -161,7 +158,8 @@ private: // a width <= ROUNDING_WIDTH_CAP static const int32_t ROUNDING_WIDTH_CAP = 1080; static bool roundBufferDimensionNearest(int32_t width, int32_t height, int32_t format, - const CameraMetadata& info, /*out*/int32_t* outWidth, /*out*/int32_t* outHeight); + android_dataspace dataSpace, const CameraMetadata& info, + /*out*/int32_t* outWidth, /*out*/int32_t* outHeight); // IGraphicsBufferProducer binder -> Stream ID KeyedVector<sp<IBinder>, int> mStreamMap; diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp deleted file mode 100644 index 59e5083..0000000 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2013 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 "ProCamera2Client" -#define ATRACE_TAG ATRACE_TAG_CAMERA -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include <utils/Trace.h> - -#include <cutils/properties.h> -#include <gui/Surface.h> -#include <gui/Surface.h> - -#include "api_pro/ProCamera2Client.h" -#include "common/CameraDeviceBase.h" - -namespace android { -using namespace camera2; - -// Interface used by CameraService - -ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid) : - Camera2ClientBase(cameraService, remoteCallback, clientPackageName, - cameraId, cameraFacing, clientPid, clientUid, servicePid) -{ - ATRACE_CALL(); - ALOGI("ProCamera %d: Opened", cameraId); - - mExclusiveLock = false; -} - -status_t ProCamera2Client::initialize(camera_module_t *module) -{ - ATRACE_CALL(); - status_t res; - - res = Camera2ClientBase::initialize(module); - if (res != OK) { - return res; - } - - String8 threadName; - mFrameProcessor = new FrameProcessorBase(mDevice); - threadName = String8::format("PC2-%d-FrameProc", mCameraId); - mFrameProcessor->run(threadName.string()); - - mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID, - FRAME_PROCESSOR_LISTENER_MAX_ID, - /*listener*/this); - - return OK; -} - -ProCamera2Client::~ProCamera2Client() { -} - -status_t ProCamera2Client::exclusiveTryLock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (!mDevice.get()) return PERMISSION_DENIED; - - if (!mExclusiveLock) { - mExclusiveLock = true; - - if (mRemoteCallback != NULL) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_ACQUIRED); - } - - ALOGV("%s: exclusive lock acquired", __FUNCTION__); - - return OK; - } - - // TODO: have a PERMISSION_DENIED case for when someone else owns the lock - - // don't allow recursive locking - ALOGW("%s: exclusive lock already exists - recursive locking is not" - "allowed", __FUNCTION__); - - return ALREADY_EXISTS; -} - -status_t ProCamera2Client::exclusiveLock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (!mDevice.get()) return PERMISSION_DENIED; - - /** - * TODO: this should asynchronously 'wait' until the lock becomes available - * if another client already has an exclusive lock. - * - * once we have proper sharing support this will need to do - * more than just return immediately - */ - if (!mExclusiveLock) { - mExclusiveLock = true; - - if (mRemoteCallback != NULL) { - mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED); - } - - ALOGV("%s: exclusive lock acquired", __FUNCTION__); - - return OK; - } - - // don't allow recursive locking - ALOGW("%s: exclusive lock already exists - recursive locking is not allowed" - , __FUNCTION__); - return ALREADY_EXISTS; -} - -status_t ProCamera2Client::exclusiveUnlock() { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - // don't allow unlocking if we have no lock - if (!mExclusiveLock) { - ALOGW("%s: cannot unlock, no lock was held in the first place", - __FUNCTION__); - return BAD_VALUE; - } - - mExclusiveLock = false; - if (mRemoteCallback != NULL ) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_RELEASED); - } - ALOGV("%s: exclusive lock released", __FUNCTION__); - - return OK; -} - -bool ProCamera2Client::hasExclusiveLock() { - Mutex::Autolock icl(mBinderSerializationLock); - return mExclusiveLock; -} - -void ProCamera2Client::onExclusiveLockStolen() { - ALOGV("%s: ProClient lost exclusivity (id %d)", - __FUNCTION__, mCameraId); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (mExclusiveLock && mRemoteCallback.get() != NULL) { - mRemoteCallback->onLockStatusChanged( - IProCameraCallbacks::LOCK_STOLEN); - } - - mExclusiveLock = false; - - //TODO: we should not need to detach the device, merely reset it. - detachDevice(); -} - -status_t ProCamera2Client::submitRequest(camera_metadata_t* request, - bool streaming) { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - if (!mExclusiveLock) { - return PERMISSION_DENIED; - } - - CameraMetadata metadata(request); - - if (!enforceRequestPermissions(metadata)) { - return PERMISSION_DENIED; - } - - if (streaming) { - return mDevice->setStreamingRequest(metadata); - } else { - return mDevice->capture(metadata); - } - - // unreachable. thx gcc for a useless warning - return OK; -} - -status_t ProCamera2Client::cancelRequest(int requestId) { - (void)requestId; - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - if (!mExclusiveLock) { - return PERMISSION_DENIED; - } - - // TODO: implement - ALOGE("%s: not fully implemented yet", __FUNCTION__); - return INVALID_OPERATION; -} - -status_t ProCamera2Client::deleteStream(int streamId) { - ATRACE_CALL(); - ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId); - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - mDevice->clearStreamingRequest(); - - status_t code; - if ((code = mDevice->waitUntilDrained()) != OK) { - ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code); - } - - return mDevice->deleteStream(streamId); -} - -status_t ProCamera2Client::createStream(int width, int height, int format, - const sp<IGraphicBufferProducer>& bufferProducer, - /*out*/ - int* streamId) -{ - if (streamId) { - *streamId = -1; - } - - ATRACE_CALL(); - ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format); - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - sp<IBinder> binder; - sp<ANativeWindow> window; - if (bufferProducer != 0) { - binder = IInterface::asBinder(bufferProducer); - window = new Surface(bufferProducer); - } - - return mDevice->createStream(window, width, height, format, - streamId); -} - -// Create a request object from a template. -// -- Caller owns the newly allocated metadata -status_t ProCamera2Client::createDefaultRequest(int templateId, - /*out*/ - camera_metadata** request) -{ - ATRACE_CALL(); - ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId); - - if (request) { - *request = NULL; - } - - status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - CameraMetadata metadata; - if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) { - *request = metadata.release(); - } - - return res; -} - -status_t ProCamera2Client::getCameraInfo(int cameraId, - /*out*/ - camera_metadata** info) -{ - if (cameraId != mCameraId) { - return INVALID_OPERATION; - } - - Mutex::Autolock icl(mBinderSerializationLock); - - if (!mDevice.get()) return DEAD_OBJECT; - - CameraMetadata deviceInfo = mDevice->info(); - *info = deviceInfo.release(); - - return OK; -} - -status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) { - String8 result; - result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n", - mCameraId, - (getRemoteCallback() != NULL ? - IInterface::asBinder(getRemoteCallback()).get() : NULL), - mClientPid); - result.append(" State:\n"); - write(fd, result.string(), result.size()); - - // TODO: print dynamic/request section from most recent requests - mFrameProcessor->dump(fd, args); - return dumpDevice(fd, args); -} - -// IProCameraUser interface - -void ProCamera2Client::detachDevice() { - if (mDevice == 0) return; - - ALOGV("Camera %d: Stopping processors", mCameraId); - - mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID, - FRAME_PROCESSOR_LISTENER_MAX_ID, - /*listener*/this); - mFrameProcessor->requestExit(); - ALOGV("Camera %d: Waiting for threads", mCameraId); - mFrameProcessor->join(); - ALOGV("Camera %d: Disconnecting device", mCameraId); - - // WORKAROUND: HAL refuses to disconnect while there's streams in flight - { - mDevice->clearStreamingRequest(); - - status_t code; - if ((code = mDevice->waitUntilDrained()) != OK) { - ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, - code); - } - } - - Camera2ClientBase::detachDevice(); -} - -void ProCamera2Client::onResultAvailable(const CaptureResult& result) { - ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); - - Mutex::Autolock icl(mBinderSerializationLock); - SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); - - if (mRemoteCallback != NULL) { - CameraMetadata tmp(result.mMetadata); - camera_metadata_t* meta = tmp.release(); - ALOGV("%s: meta = %p ", __FUNCTION__, meta); - mRemoteCallback->onResultReceived(result.mResultExtras.requestId, meta); - tmp.acquire(meta); - } -} - -bool ProCamera2Client::enforceRequestPermissions(CameraMetadata& metadata) { - - const int pid = IPCThreadState::self()->getCallingPid(); - const int selfPid = getpid(); - camera_metadata_entry_t entry; - - /** - * Mixin default important security values - * - android.led.transmit = defaulted ON - */ - CameraMetadata staticInfo = mDevice->info(); - entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS); - for(size_t i = 0; i < entry.count; ++i) { - uint8_t led = entry.data.u8[i]; - - switch(led) { - case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: { - uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON; - if (!metadata.exists(ANDROID_LED_TRANSMIT)) { - metadata.update(ANDROID_LED_TRANSMIT, - &transmitDefault, 1); - } - break; - } - } - } - - // We can do anything! - if (pid == selfPid) { - return true; - } - - /** - * Permission check special fields in the request - * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT - */ - entry = metadata.find(ANDROID_LED_TRANSMIT); - if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) { - String16 permissionString = - String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED"); - if (!checkCallingPermission(permissionString)) { - const int uid = IPCThreadState::self()->getCallingUid(); - ALOGE("Permission Denial: " - "can't disable transmit LED pid=%d, uid=%d", pid, uid); - return false; - } - } - - return true; -} - -} // namespace android diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h deleted file mode 100644 index 9d83122..0000000 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H -#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H - -#include "CameraService.h" -#include "common/FrameProcessorBase.h" -#include "common/Camera2ClientBase.h" -#include "device2/Camera2Device.h" -#include "camera/CaptureResult.h" - -namespace android { - -class IMemory; -/** - * Implements the binder IProCameraUser API, - * meant for HAL2-level private API access. - */ -class ProCamera2Client : - public Camera2ClientBase<CameraService::ProClient>, - public camera2::FrameProcessorBase::FilteredListener -{ -public: - /** - * IProCameraUser interface (see IProCameraUser for details) - */ - virtual status_t exclusiveTryLock(); - virtual status_t exclusiveLock(); - virtual status_t exclusiveUnlock(); - - virtual bool hasExclusiveLock(); - - // Note that the callee gets a copy of the metadata. - virtual int submitRequest(camera_metadata_t* metadata, - bool streaming = false); - virtual status_t cancelRequest(int requestId); - - virtual status_t deleteStream(int streamId); - - virtual status_t createStream( - int width, - int height, - int format, - const sp<IGraphicBufferProducer>& bufferProducer, - /*out*/ - int* streamId); - - // Create a request object from a template. - // -- Caller owns the newly allocated metadata - virtual status_t createDefaultRequest(int templateId, - /*out*/ - camera_metadata** request); - - // Get the static metadata for the camera - // -- Caller owns the newly allocated metadata - virtual status_t getCameraInfo(int cameraId, - /*out*/ - camera_metadata** info); - - /** - * Interface used by CameraService - */ - - ProCamera2Client(const sp<CameraService>& cameraService, - const sp<IProCameraCallbacks>& remoteCallback, - const String16& clientPackageName, - int cameraId, - int cameraFacing, - int clientPid, - uid_t clientUid, - int servicePid); - virtual ~ProCamera2Client(); - - virtual status_t initialize(camera_module_t *module); - - virtual status_t dump(int fd, const Vector<String16>& args); - - // Callbacks from camera service - virtual void onExclusiveLockStolen(); - - /** - * Interface used by independent components of ProCamera2Client. - */ - -protected: - /** FilteredListener implementation **/ - virtual void onResultAvailable(const CaptureResult& result); - - virtual void detachDevice(); - -private: - /** IProCameraUser interface-related private members */ - - /** Preview callback related members */ - sp<camera2::FrameProcessorBase> mFrameProcessor; - static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0; - static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL; - - /** Utility members */ - bool enforceRequestPermissions(CameraMetadata& metadata); - - // Whether or not we have an exclusive lock on the device - // - if no we can't modify the request queue. - // note that creating/deleting streams we own is still OK - bool mExclusiveLock; -}; - -}; // namespace android - -#endif diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index 453c8bd..c0c2314 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -78,7 +78,7 @@ status_t Camera2ClientBase<TClientBase>::checkPid(const char* checkLocation) } template <typename TClientBase> -status_t Camera2ClientBase<TClientBase>::initialize(camera_module_t *module) { +status_t Camera2ClientBase<TClientBase>::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing client for camera %d", __FUNCTION__, TClientBase::mCameraId); @@ -337,7 +337,6 @@ void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() { mRemoteCallback.clear(); } -template class Camera2ClientBase<CameraService::ProClient>; template class Camera2ClientBase<CameraService::Client>; template class Camera2ClientBase<CameraDeviceClientBase>; diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index e09c1b5..168ea0a 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -18,6 +18,7 @@ #define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H #include "common/CameraDeviceBase.h" +#include "common/CameraModule.h" #include "camera/CaptureResult.h" namespace android { @@ -35,7 +36,7 @@ public: typedef typename TClientBase::TCamCallbacks TCamCallbacks; /** - * Base binder interface (see ICamera/IProCameraUser for details) + * Base binder interface (see ICamera/ICameraDeviceUser for details) */ virtual status_t connect(const sp<TCamCallbacks>& callbacks); virtual void disconnect(); @@ -55,7 +56,7 @@ public: int servicePid); virtual ~Camera2ClientBase(); - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t dump(int fd, const Vector<String16>& args); /** diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index d26e20c..fe55b9e 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -29,6 +29,7 @@ #include "hardware/camera3.h" #include "camera/CameraMetadata.h" #include "camera/CaptureResult.h" +#include "common/CameraModule.h" namespace android { @@ -45,7 +46,7 @@ class CameraDeviceBase : public virtual RefBase { */ virtual int getId() const = 0; - virtual status_t initialize(camera_module_t *module) = 0; + virtual status_t initialize(CameraModule *module) = 0; virtual status_t disconnect() = 0; virtual status_t dump(int fd, const Vector<String16> &args) = 0; @@ -99,17 +100,14 @@ class CameraDeviceBase : public virtual RefBase { nsecs_t timeout) = 0; /** - * Create an output stream of the requested size and format. + * Create an output stream of the requested size, format, rotation and dataspace * - * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects - * an appropriate format; it can be queried with getStreamInfo. - * - * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be - * equal to the size in bytes of the buffers to allocate for the stream. For - * other formats, the size parameter is ignored. + * For HAL_PIXEL_FORMAT_BLOB formats, the width and height should be the + * logical dimensions of the buffer, not the number of bytes. */ virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) = 0; + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0; /** * Create an input reprocess stream that uses buffers from an existing diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp new file mode 100644 index 0000000..e5b12ae --- /dev/null +++ b/services/camera/libcameraservice/common/CameraModule.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 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 "CameraModule" +//#define LOG_NDEBUG 0 + +#include "CameraModule.h" + +namespace android { + +void CameraModule::deriveCameraCharacteristicsKeys( + uint32_t deviceVersion, CameraMetadata &chars) { + // HAL1 devices should not reach here + if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) { + ALOGV("%s: Cannot derive keys for HAL version < 2.0"); + return; + } + + // Keys added in HAL3.3 + if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) { + Vector<uint8_t> controlModes; + uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE; + chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1); + data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE; + chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1); + controlModes.push(ANDROID_CONTROL_MODE_OFF); + controlModes.push(ANDROID_CONTROL_MODE_AUTO); + camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) { + controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE); + } + chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes); + } + return; +} + +CameraModule::CameraModule(camera_module_t *module) { + if (module == NULL) { + ALOGE("%s: camera hardware module must not be null", __FUNCTION__); + assert(0); + } + + mModule = module; + mCameraInfoMap.setCapacity(getNumberOfCameras()); +} + +int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) { + Mutex::Autolock lock(mCameraInfoLock); + if (cameraId < 0) { + ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId); + return -EINVAL; + } + + // Only override static_camera_characteristics for API2 devices + int apiVersion = mModule->common.module_api_version; + if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) { + return mModule->get_camera_info(cameraId, info); + } + + ssize_t index = mCameraInfoMap.indexOfKey(cameraId); + if (index == NAME_NOT_FOUND) { + // Get camera info from raw module and cache it + camera_info rawInfo, cameraInfo; + int ret = mModule->get_camera_info(cameraId, &rawInfo); + if (ret != 0) { + return ret; + } + CameraMetadata m; + m = rawInfo.static_camera_characteristics; + deriveCameraCharacteristicsKeys(rawInfo.device_version, m); + mCameraCharacteristicsMap.add(cameraId, m); + cameraInfo = rawInfo; + cameraInfo.static_camera_characteristics = + mCameraCharacteristicsMap.valueFor(cameraId).getAndLock(); + mCameraInfoMap.add(cameraId, cameraInfo); + index = mCameraInfoMap.indexOfKey(cameraId); + } + + assert(index != NAME_NOT_FOUND); + // return the cached camera info + *info = mCameraInfoMap[index]; + return 0; +} + +int CameraModule::open(const char* id, struct hw_device_t** device) { + return filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device)); +} + +int CameraModule::openLegacy( + const char* id, uint32_t halVersion, struct hw_device_t** device) { + return mModule->open_legacy(&mModule->common, id, halVersion, device); +} + +int CameraModule::getNumberOfCameras() { + return mModule->get_number_of_cameras(); +} + +int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) { + return mModule->set_callbacks(callbacks); +} + +bool CameraModule::isVendorTagDefined() { + return mModule->get_vendor_tag_ops != NULL; +} + +void CameraModule::getVendorTagOps(vendor_tag_ops_t* ops) { + if (mModule->get_vendor_tag_ops) { + mModule->get_vendor_tag_ops(ops); + } +} + +int CameraModule::setTorchMode(const char* camera_id, bool enable) { + return mModule->set_torch_mode(camera_id, enable); +} + +status_t CameraModule::filterOpenErrorCode(status_t err) { + switch(err) { + case NO_ERROR: + case -EBUSY: + case -EINVAL: + case -EUSERS: + return err; + default: + break; + } + return -ENODEV; +} + +uint16_t CameraModule::getModuleApiVersion() { + return mModule->common.module_api_version; +} + +const char* CameraModule::getModuleName() { + return mModule->common.name; +} + +uint16_t CameraModule::getHalApiVersion() { + return mModule->common.hal_api_version; +} + +const char* CameraModule::getModuleAuthor() { + return mModule->common.author; +} + +void* CameraModule::getDso() { + return mModule->common.dso; +} + +}; // namespace android + diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h new file mode 100644 index 0000000..e285b21 --- /dev/null +++ b/services/camera/libcameraservice/common/CameraModule.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERAMODULE_H +#define ANDROID_SERVERS_CAMERA_CAMERAMODULE_H + +#include <hardware/camera.h> +#include <camera/CameraMetadata.h> +#include <utils/Mutex.h> +#include <utils/KeyedVector.h> + +namespace android { +/** + * A wrapper class for HAL camera module. + * + * This class wraps camera_module_t returned from HAL to provide a wrapped + * get_camera_info implementation which CameraService generates some + * camera characteristics keys defined in newer HAL version on an older HAL. + */ +class CameraModule { +public: + CameraModule(camera_module_t *module); + + int getCameraInfo(int cameraId, struct camera_info *info); + int getNumberOfCameras(void); + int open(const char* id, struct hw_device_t** device); + int openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device); + int setCallbacks(const camera_module_callbacks_t *callbacks); + bool isVendorTagDefined(); + void getVendorTagOps(vendor_tag_ops_t* ops); + int setTorchMode(const char* camera_id, bool enable); + uint16_t getModuleApiVersion(); + const char* getModuleName(); + uint16_t getHalApiVersion(); + const char* getModuleAuthor(); + // Only used by CameraModuleFixture native test. Do NOT use elsewhere. + void *getDso(); + +private: + // Derive camera characteristics keys defined after HAL device version + static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars); + status_t filterOpenErrorCode(status_t err); + + camera_module_t *mModule; + KeyedVector<int, camera_info> mCameraInfoMap; + KeyedVector<int, CameraMetadata> mCameraCharacteristicsMap; + Mutex mCameraInfoLock; +}; + +} // namespace android + +#endif + diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h index 1935c2b..7f14cd4 100644 --- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h @@ -89,24 +89,22 @@ public: } } - status_t initialize(hw_module_t *module) + status_t initialize(CameraModule *module) { ALOGI("Opening camera %s", mName.string()); - camera_module_t *cameraModule = reinterpret_cast<camera_module_t *>(module); camera_info info; - status_t res = cameraModule->get_camera_info(atoi(mName.string()), &info); + status_t res = module->getCameraInfo(atoi(mName.string()), &info); if (res != OK) return res; int rc = OK; - if (module->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 && + if (module->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_3 && info.device_version > CAMERA_DEVICE_API_VERSION_1_0) { // Open higher version camera device as HAL1.0 device. - rc = cameraModule->open_legacy(module, mName.string(), - CAMERA_DEVICE_API_VERSION_1_0, - (hw_device_t **)&mDevice); + rc = module->openLegacy(mName.string(), + CAMERA_DEVICE_API_VERSION_1_0, + (hw_device_t **)&mDevice); } else { - rc = CameraService::filterOpenErrorCode(module->methods->open( - module, mName.string(), (hw_device_t **)&mDevice)); + rc = module->open(mName.string(), (hw_device_t **)&mDevice); } if (rc != OK) { ALOGE("Could not open camera %s: %d", mName.string(), rc); diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index d1158d6..878986b 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -53,7 +53,7 @@ int Camera2Device::getId() const { return mId; } -status_t Camera2Device::initialize(camera_module_t *module) +status_t Camera2Device::initialize(CameraModule *module) { ATRACE_CALL(); ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId); @@ -68,8 +68,7 @@ status_t Camera2Device::initialize(camera_module_t *module) camera2_device_t *device; - res = CameraService::filterOpenErrorCode(module->common.methods->open( - &module->common, name, reinterpret_cast<hw_device_t**>(&device))); + res = module->open(name, reinterpret_cast<hw_device_t**>(&device)); if (res != OK) { ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, @@ -87,7 +86,7 @@ status_t Camera2Device::initialize(camera_module_t *module) } camera_info info; - res = module->get_camera_info(mId, &info); + res = module->getCameraInfo(mId, &info); if (res != OK ) return res; if (info.device_version != device->common.version) { @@ -242,7 +241,8 @@ status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time } status_t Camera2Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) { + uint32_t width, uint32_t height, int format, + android_dataspace /*dataSpace*/, camera3_stream_rotation_t rotation, int *id) { ATRACE_CALL(); status_t res; ALOGV("%s: E", __FUNCTION__); diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 4def8ae..9b32fa6 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -43,7 +43,7 @@ class Camera2Device: public CameraDeviceBase { * CameraDevice interface */ virtual int getId() const; - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16>& args); virtual const CameraMetadata& info() const; @@ -57,7 +57,8 @@ class Camera2Device: public CameraDeviceBase { virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); virtual status_t getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format); diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 53e6fa9..8236788 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -86,7 +86,7 @@ int Camera3Device::getId() const { * CameraDeviceBase interface */ -status_t Camera3Device::initialize(camera_module_t *module) +status_t Camera3Device::initialize(CameraModule *module) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); @@ -106,9 +106,8 @@ status_t Camera3Device::initialize(camera_module_t *module) camera3_device_t *device; ATRACE_BEGIN("camera3->open"); - res = CameraService::filterOpenErrorCode(module->common.methods->open( - &module->common, deviceName.string(), - reinterpret_cast<hw_device_t**>(&device))); + res = module->open(deviceName.string(), + reinterpret_cast<hw_device_t**>(&device)); ATRACE_END(); if (res != OK) { @@ -127,7 +126,7 @@ status_t Camera3Device::initialize(camera_module_t *module) } camera_info info; - res = CameraService::filterGetInfoErrorCode(module->get_camera_info( + res = CameraService::filterGetInfoErrorCode(module->getCameraInfo( mId, &info)); if (res != OK) return res; @@ -802,12 +801,13 @@ status_t Camera3Device::createZslStream( } status_t Camera3Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id) { + uint32_t width, uint32_t height, int format, android_dataspace dataSpace, + camera3_stream_rotation_t rotation, int *id) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); - ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d", - mId, mNextStreamId, width, height, format); + ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d", + mId, mNextStreamId, width, height, format, dataSpace, rotation); status_t res; bool wasActive = false; @@ -847,10 +847,10 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer, } newStream = new Camera3OutputStream(mNextStreamId, consumer, - width, height, jpegBufferSize, format); + width, height, jpegBufferSize, format, dataSpace, rotation); } else { newStream = new Camera3OutputStream(mNextStreamId, consumer, - width, height, format); + width, height, format, dataSpace, rotation); } newStream->setStatusTracker(mStatusTracker); diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index ec8dc10..a77548d 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -73,7 +73,7 @@ class Camera3Device : virtual int getId() const; // Transitions to idle state on success. - virtual status_t initialize(camera_module_t *module); + virtual status_t initialize(CameraModule *module); virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16> &args); virtual const CameraMetadata& info() const; @@ -95,7 +95,8 @@ class Camera3Device : // If adding streams while actively capturing, will pause device before adding // stream, reconfiguring device, and unpausing. virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, int *id); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id); virtual status_t createInputStream( uint32_t width, uint32_t height, int format, int *id); diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp index 6656b09..01edfff 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp @@ -28,7 +28,7 @@ namespace camera3 { Camera3DummyStream::Camera3DummyStream(int id) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT, - /*maxSize*/0, DUMMY_FORMAT) { + /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION) { } diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h index 3e42623..d023c57 100644 --- a/services/camera/libcameraservice/device3/Camera3DummyStream.h +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h @@ -75,6 +75,8 @@ class Camera3DummyStream : static const int DUMMY_WIDTH = 320; static const int DUMMY_HEIGHT = 240; static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN; + static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0; static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER; /** diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index cc66459..8696413 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -30,9 +30,10 @@ namespace android { namespace camera3 { Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3Stream(id, type, - width, height, maxSize, format), + width, height, maxSize, format, dataSpace, rotation), mTotalBufferCount(0), mHandoutTotalBufferCount(0), mHandoutOutputBufferCount(0), diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h index a35c290..abcf2b1 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h @@ -33,7 +33,8 @@ class Camera3IOStreamBase : public Camera3Stream { protected: Camera3IOStreamBase(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); public: diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 319be1d..6bf671e 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -18,6 +18,7 @@ #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 +#include <gui/BufferItem.h> #include <utils/Log.h> #include <utils/Trace.h> #include "Camera3InputStream.h" @@ -28,8 +29,8 @@ namespace camera3 { Camera3InputStream::Camera3InputStream(int id, uint32_t width, uint32_t height, int format) : - Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, - /*maxSize*/0, format) { + Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0, + format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0) { if (format == HAL_PIXEL_FORMAT_BLOB) { ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__); diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h index ae49467..fd17f4f 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.h +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -48,8 +48,6 @@ class Camera3InputStream : public Camera3IOStreamBase { private: - typedef BufferItemConsumer::BufferItem BufferItem; - sp<BufferItemConsumer> mConsumer; Vector<BufferItem> mBuffersInFlight; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 77ad503..0c739e9 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -33,9 +33,10 @@ namespace camera3 { Camera3OutputStream::Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format) : + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, - /*maxSize*/0, format), + /*maxSize*/0, format, dataSpace, rotation), mConsumer(consumer), mTransform(0), mTraceFirstBuffer(true) { @@ -48,9 +49,10 @@ Camera3OutputStream::Camera3OutputStream(int id, Camera3OutputStream::Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize, - format), + format, dataSpace, rotation), mConsumer(consumer), mTransform(0), mTraceFirstBuffer(true) { @@ -69,10 +71,12 @@ Camera3OutputStream::Camera3OutputStream(int id, Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type, uint32_t width, uint32_t height, - int format) : + int format, + android_dataspace dataSpace, + camera3_stream_rotation_t rotation) : Camera3IOStreamBase(id, type, width, height, /*maxSize*/0, - format), + format, dataSpace, rotation), mTransform(0) { // Subclasses expected to initialize mConsumer themselves @@ -153,33 +157,9 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( ALOG_ASSERT(output, "Expected output to be true"); status_t res; - sp<Fence> releaseFence; - - /** - * Fence management - calculate Release Fence - */ - if (buffer.status == CAMERA3_BUFFER_STATUS_ERROR) { - if (buffer.release_fence != -1) { - ALOGE("%s: Stream %d: HAL should not set release_fence(%d) when " - "there is an error", __FUNCTION__, mId, buffer.release_fence); - close(buffer.release_fence); - } - - /** - * Reassign release fence as the acquire fence in case of error - */ - releaseFence = new Fence(buffer.acquire_fence); - } else { - res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp); - if (res != OK) { - ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - return res; - } - - releaseFence = new Fence(buffer.release_fence); - } + // Fence management - always honor release fence from HAL + sp<Fence> releaseFence = new Fence(buffer.release_fence); int anwReleaseFence = releaseFence->dup(); /** @@ -213,6 +193,13 @@ status_t Camera3OutputStream::returnBufferCheckedLocked( mTraceFirstBuffer = false; } + res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp); + if (res != OK) { + ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + res = currentConsumer->queueBuffer(currentConsumer.get(), container_of(buffer.buffer, ANativeWindowBuffer, handle), anwReleaseFence); @@ -323,6 +310,14 @@ status_t Camera3OutputStream::configureQueueLocked() { return res; } + res = native_window_set_buffers_data_space(mConsumer.get(), + camera3_stream::data_space); + if (res != OK) { + ALOGE("%s: Unable to configure stream dataspace %#x for stream %d", + __FUNCTION__, camera3_stream::data_space, mId); + return res; + } + int maxConsumerBuffers; res = mConsumer->query(mConsumer.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers); diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index be278c5..12b2ebb 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -39,14 +39,16 @@ class Camera3OutputStream : * Set up a stream for formats that have 2 dimensions, such as RAW and YUV. */ Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Set up a stream for formats that have a variable buffer size for the same * dimensions, such as compressed JPEG. */ Camera3OutputStream(int id, sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); virtual ~Camera3OutputStream(); @@ -64,7 +66,8 @@ class Camera3OutputStream : protected: Camera3OutputStream(int id, camera3_stream_type_t type, - uint32_t width, uint32_t height, int format); + uint32_t width, uint32_t height, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Note that we release the lock briefly in this function diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 3c0e908..4acbce3 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -46,7 +46,8 @@ const Camera3Stream* Camera3Stream::cast(const camera3_stream *stream) { Camera3Stream::Camera3Stream(int id, camera3_stream_type type, - uint32_t width, uint32_t height, size_t maxSize, int format) : + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation) : camera3_stream(), mId(id), mName(String8::format("Camera3Stream[%d]", id)), @@ -58,6 +59,8 @@ Camera3Stream::Camera3Stream(int id, camera3_stream::width = width; camera3_stream::height = height; camera3_stream::format = format; + camera3_stream::data_space = dataSpace; + camera3_stream::rotation = rotation; camera3_stream::usage = 0; camera3_stream::max_buffers = 0; camera3_stream::priv = NULL; @@ -84,6 +87,10 @@ int Camera3Stream::getFormat() const { return camera3_stream::format; } +android_dataspace Camera3Stream::getDataSpace() const { + return camera3_stream::data_space; +} + camera3_stream* Camera3Stream::startConfiguration() { ATRACE_CALL(); Mutex::Autolock l(mLock); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index d0e1337..aba27fe 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -119,9 +119,10 @@ class Camera3Stream : /** * Get the stream's dimensions and format */ - uint32_t getWidth() const; - uint32_t getHeight() const; - int getFormat() const; + uint32_t getWidth() const; + uint32_t getHeight() const; + int getFormat() const; + android_dataspace getDataSpace() const; /** * Start the stream configuration process. Returns a handle to the stream's @@ -264,7 +265,8 @@ class Camera3Stream : mutable Mutex mLock; Camera3Stream(int id, camera3_stream_type type, - uint32_t width, uint32_t height, size_t maxSize, int format); + uint32_t width, uint32_t height, size_t maxSize, int format, + android_dataspace dataSpace, camera3_stream_rotation_t rotation); /** * Interface to be implemented by derived classes diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp index 81330ea..10d7f2e 100644 --- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp @@ -114,7 +114,8 @@ Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height, int bufferCount) : Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL, width, height, - HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0), mDepth(bufferCount) { sp<IGraphicBufferProducer> producer; diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp index d0f29de..8cd6800 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp @@ -268,7 +268,7 @@ status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) { return OK; } -void RingBufferConsumer::onFrameAvailable(const android::BufferItem& item) { +void RingBufferConsumer::onFrameAvailable(const BufferItem& item) { status_t err; { diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h index 90fd734..83e7298 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.h +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_GUI_RINGBUFFERCONSUMER_H #define ANDROID_GUI_RINGBUFFERCONSUMER_H +#include <gui/BufferItem.h> #include <gui/ConsumerBase.h> #include <ui/GraphicBuffer.h> @@ -54,8 +55,6 @@ class RingBufferConsumer : public ConsumerBase, public: typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; - typedef BufferQueue::BufferItem BufferItem; - enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; @@ -165,7 +164,7 @@ class RingBufferConsumer : public ConsumerBase, private: // Override ConsumerBase::onFrameAvailable - virtual void onFrameAvailable(const android::BufferItem& item); + virtual void onFrameAvailable(const BufferItem& item); void pinBufferLocked(const BufferItem& item); void unpinBuffer(const BufferItem& item); diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp new file mode 100644 index 0000000..c8ee965 --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 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. + */ + +#include "AutoConditionLock.h" + +namespace android { + +WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {} + +WaitableMutexWrapper::~WaitableMutexWrapper() {} + +// Locks manager-owned mutex +AutoConditionLock::AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager) : + mManager{manager}, mAutoLock{manager->mMutex} {} + +// Unlocks manager-owned mutex +AutoConditionLock::~AutoConditionLock() { + // Unset the condition and wake everyone up before releasing lock + mManager->mState = false; + mManager->mCondition.broadcast(); +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + + // Figure out what time in the future we should hit the timeout + nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime; + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC); + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager) { + + if (manager == nullptr || manager->mMutex == nullptr) { + // Bad input, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + + // Acquire scoped lock + std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager)); + + // Wait until we timeout, or success + while(manager->mState) { + status_t ret = manager->mCondition.wait(*(manager->mMutex)); + if (ret != NO_ERROR) { + // Timed out or whatever, return null + return std::unique_ptr<AutoConditionLock>{nullptr}; + } + } + + // Set the condition and return + manager->mState = true; + return scopedLock; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h new file mode 100644 index 0000000..9a3eafc --- /dev/null +++ b/services/camera/libcameraservice/utils/AutoConditionLock.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 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. + */ + + +#ifndef ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H +#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H + +#include <utils/Timers.h> +#include <utils/Condition.h> +#include <utils/Errors.h> +#include <utils/Mutex.h> + +#include <memory> + +namespace android { + +/** + * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the + * wrapped Mutex with timeouts for lock acquisition. + */ +class WaitableMutexWrapper { + friend class AutoConditionLock; +public: + /** + * Construct the ConditionManger with the given Mutex. + */ + WaitableMutexWrapper(Mutex* mutex); + + virtual ~WaitableMutexWrapper(); +private: + Mutex* mMutex; + bool mState; + Condition mCondition; +}; + +/** + * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be + * specified for lock acquisition. + * + * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's + * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this, + * call AutoConditionLock::waitAndAcquire to get an instance. This will: + * - Lock the given WaitableMutexWrapper's mutex. + * - Wait for the WaitableMutexWrapper's condition to become false, or timeout. + * - Set the WaitableMutexWrapper's condition to true. + * + * When the AutoConditionLock goes out of scope and is destroyed, this will: + * - Set the WaitableMutexWrapper's condition to false. + * - Signal threads waiting on this condition to wakeup. + * - Release WaitableMutexWrapper's mutex. + */ +class AutoConditionLock final { +public: + AutoConditionLock() = delete; + AutoConditionLock(const AutoConditionLock& other) = delete; + AutoConditionLock & operator=(const AutoConditionLock&) = delete; + + ~AutoConditionLock(); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime + * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails, or a timeout occurs. + */ + static std::unique_ptr<AutoConditionLock> waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime); + + /** + * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to + * acquire the WaitableMutexWrapper's wrapped lock. + * + * Return an empty unique_ptr if this fails. + */ + static std::unique_ptr<AutoConditionLock> waitAndAcquire( + const std::shared_ptr<WaitableMutexWrapper>& manager); +private: + AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager); + + std::shared_ptr<WaitableMutexWrapper> mManager; + Mutex::Autolock mAutoLock; +}; + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h new file mode 100644 index 0000000..ad5486d --- /dev/null +++ b/services/camera/libcameraservice/utils/ClientManager.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H +#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H + +#include <utils/Mutex.h> + +#include <algorithm> +#include <utility> +#include <vector> +#include <set> +#include <map> +#include <memory> + +namespace android { +namespace resource_policy { + +// -------------------------------------------------------------------------------- + +/** + * The ClientDescriptor class is a container for a given key/value pair identifying a shared + * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used + * in determining eviction behavior. + * + * Aside from the priority, these values are immutable once the ClientDescriptor has been + * constructed. + */ +template<class KEY, class VALUE> +class ClientDescriptor final { +public: + ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId); + ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys, + int32_t priority, int32_t ownerId); + + ~ClientDescriptor(); + + /** + * Return the key for this descriptor. + */ + const KEY& getKey() const; + + /** + * Return the value for this descriptor. + */ + const VALUE& getValue() const; + + /** + * Return the cost for this descriptor. + */ + int32_t getCost() const; + + /** + * Return the priority for this descriptor. + */ + int32_t getPriority() const; + + /** + * Return the owner ID for this descriptor. + */ + int32_t getOwnerId() const; + + /** + * Return true if the given key is in this descriptor's conflicting keys list. + */ + bool isConflicting(const KEY& key) const; + + /** + * Return the set of all conflicting keys for this descriptor. + */ + std::set<KEY> getConflicting() const; + + /** + * Set the proirity for this descriptor. + */ + void setPriority(int32_t priority); + + // This class is ordered by key + template<class K, class V> + friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b); + +private: + KEY mKey; + VALUE mValue; + int32_t mCost; + std::set<KEY> mConflicting; + int32_t mPriority; + int32_t mOwnerId; +}; // class ClientDescriptor + +template<class K, class V> +bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) { + return a.mKey < b.mKey; +} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost, + const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key}, + mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority}, + mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, + std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) : + mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost}, + mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority}, + mOwnerId{ownerId} {} + +template<class KEY, class VALUE> +ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {} + +template<class KEY, class VALUE> +const KEY& ClientDescriptor<KEY, VALUE>::getKey() const { + return mKey; +} + +template<class KEY, class VALUE> +const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const { + return mValue; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getCost() const { + return mCost; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getPriority() const { + return mPriority; +} + +template<class KEY, class VALUE> +int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const { + return mOwnerId; +} + +template<class KEY, class VALUE> +bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const { + if (key == mKey) return true; + for (const auto& x : mConflicting) { + if (key == x) return true; + } + return false; +} + +template<class KEY, class VALUE> +std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const { + return mConflicting; +} + +template<class KEY, class VALUE> +void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) { + mPriority = priority; +} + +// -------------------------------------------------------------------------------- + +/** + * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction + * behavior for handling shared resource access. + * + * When adding a new descriptor, eviction behavior is as follows: + * - Keys are unique, adding a descriptor with the same key as an existing descriptor will + * result in the lower-priority of the two being removed. Priority ties result in the + * LRU descriptor being evicted (this means the incoming descriptor be added in this case). + * - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list + * will be removed if they have an equal or lower priority than the incoming descriptor; + * if any have a higher priority, the incoming descriptor is removed instead. + * - If the sum of all descriptors' costs, including the incoming descriptor's, is more than + * the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower + * priority, and a different owner will be evicted in LRU order until either the cost is less + * than the max cost, or all descriptors meeting this criteria have been evicted and the + * incoming descriptor has the highest priority. Otherwise, the incoming descriptor is + * removed instead. + */ +template<class KEY, class VALUE> +class ClientManager { +public: + // The default maximum "cost" allowed before evicting + static constexpr int32_t DEFAULT_MAX_COST = 100; + + ClientManager(); + ClientManager(int32_t totalCost); + + /** + * Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that + * are evicted by this action are returned in a vector. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client); + + /** + * Given a map containing owner (pid) -> priority mappings, update the priority of each + * ClientDescriptor with an owner in this mapping. + */ + void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList); + + /** + * Remove all ClientDescriptors. + */ + void removeAll(); + + /** + * Remove and return the ClientDescriptor with a given key. + */ + std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key); + + /** + * Remove the given ClientDescriptor. + */ + void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value); + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. + * + * This may return the ClientDescriptor passed in if it would be evicted. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + + /** + * Return a vector of active ClientDescriptors that prevent this client from being added. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const; + + /** + * Return a vector containing all currently active ClientDescriptors. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const; + + /** + * Return a vector containing all keys of currently active ClientDescriptors. + */ + std::vector<KEY> getAllKeys() const; + + /** + * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates + * will be removed). + */ + std::vector<int32_t> getAllOwners() const; + + /** + * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer + * if none exists. + */ + std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const; + +protected: + ~ClientManager(); + +private: + + /** + * Return a vector of the ClientDescriptors that would be evicted by adding the given + * ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the + * vector of ClientDescriptors that are higher priority than the incoming client and + * either conflict with this client, or contribute to the resource cost if that would + * prevent the incoming client from being added. + * + * This may return the ClientDescriptor passed in. + */ + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, + bool returnIncompatibleClients = false) const; + + int64_t getCurrentCostLocked() const; + + mutable Mutex mLock; + int32_t mMaxCost; + // LRU ordered, most recent at end + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients; +}; // class ClientManager + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager() : + ClientManager(DEFAULT_MAX_COST) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {} + +template<class KEY, class VALUE> +ClientManager<KEY, VALUE>::~ClientManager() {} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getIncompatibleClients( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const { + Mutex::Autolock lock(mLock); + return wouldEvictLocked(client, /*returnIncompatibleClients*/true); +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::wouldEvictLocked( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client, + bool returnIncompatibleClients) const { + + std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList; + + // Disallow null clients, return input + if (client == nullptr) { + evictList.push_back(client); + return evictList; + } + + const KEY& key = client->getKey(); + int32_t cost = client->getCost(); + int32_t priority = client->getPriority(); + int32_t owner = client->getOwnerId(); + + int64_t totalCost = getCurrentCostLocked() + cost; + + // Determine the MRU of the owners tied for having the highest priority + int32_t highestPriorityOwner = owner; + int32_t highestPriority = priority; + for (const auto& i : mClients) { + int32_t curPriority = i->getPriority(); + if (curPriority >= highestPriority) { + highestPriority = curPriority; + highestPriorityOwner = i->getOwnerId(); + } + } + + if (highestPriority == priority) { + // Switch back owner if the incoming client has the highest priority, as it is MRU + highestPriorityOwner = owner; + } + + // Build eviction list of clients to remove + for (const auto& i : mClients) { + const KEY& curKey = i->getKey(); + int32_t curCost = i->getCost(); + int32_t curPriority = i->getPriority(); + int32_t curOwner = i->getOwnerId(); + + bool conflicting = (curKey == key || i->isConflicting(key) || + client->isConflicting(curKey)); + + if (!returnIncompatibleClients) { + // Find evicted clients + + if (conflicting && curPriority > priority) { + // Pre-existing conflicting client with higher priority exists + evictList.clear(); + evictList.push_back(client); + return evictList; + } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) && + (curPriority <= priority) && + !(highestPriorityOwner == owner && owner == curOwner))) { + // Add a pre-existing client to the eviction list if: + // - We are adding a client with higher priority that conflicts with this one. + // - The total cost including the incoming client's is more than the allowable + // maximum, and the client has a non-zero cost, lower priority, and a different + // owner than the incoming client when the incoming client has the + // highest priority. + evictList.push_back(i); + totalCost -= curCost; + } + } else { + // Find clients preventing the incoming client from being added + + if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) { + // Pre-existing conflicting client with higher priority exists + evictList.push_back(i); + } + } + } + + // Immediately return the incompatible clients if we are calculating these instead + if (returnIncompatibleClients) { + return evictList; + } + + // If the total cost is too high, return the input unless the input has the highest priority + if (totalCost > mMaxCost && highestPriorityOwner != owner) { + evictList.clear(); + evictList.push_back(client); + return evictList; + } + + return evictList; + +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) { + Mutex::Autolock lock(mLock); + auto evicted = wouldEvictLocked(client); + auto it = evicted.begin(); + if (it != evicted.end() && *it == client) { + return evicted; + } + + auto iter = evicted.cbegin(); + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr->getKey() == (*iter)->getKey()) { + iter++; + return true; + } + return false; + }), mClients.end()); + + mClients.push_back(client); + + return evicted; +} + +template<class KEY, class VALUE> +std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> +ClientManager<KEY, VALUE>::getAll() const { + Mutex::Autolock lock(mLock); + return mClients; +} + +template<class KEY, class VALUE> +std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const { + Mutex::Autolock lock(mLock); + std::vector<KEY> keys(mClients.size()); + for (const auto& i : mClients) { + keys.push_back(i->getKey()); + } + return keys; +} + +template<class KEY, class VALUE> +std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const { + Mutex::Autolock lock(mLock); + std::set<int32_t> owners; + for (const auto& i : mClients) { + owners.emplace(i->getOwnerId()); + } + return std::vector<int32_t>(owners.begin(), owners.end()); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::updatePriorities( + const std::map<int32_t,int32_t>& ownerPriorityList) { + Mutex::Autolock lock(mLock); + for (auto& i : mClients) { + auto j = ownerPriorityList.find(i->getOwnerId()); + if (j != ownerPriorityList.end()) { + i->setPriority(j->second); + } + } +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get( + const KEY& key) const { + Mutex::Autolock lock(mLock); + for (const auto& i : mClients) { + if (i->getKey() == key) return i; + } + return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr); +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::removeAll() { + Mutex::Autolock lock(mLock); + mClients.clear(); +} + +template<class KEY, class VALUE> +std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) { + Mutex::Autolock lock(mLock); + + std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret; + + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr->getKey() == key) { + ret = curClientPtr; + return true; + } + return false; + }), mClients.end()); + + return ret; +} + +template<class KEY, class VALUE> +void ClientManager<KEY, VALUE>::remove( + const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) { + Mutex::Autolock lock(mLock); + // Remove evicted clients from list + mClients.erase(std::remove_if(mClients.begin(), mClients.end(), + [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) { + if (curClientPtr == value) { + return true; + } + return false; + }), mClients.end()); +} + +template<class KEY, class VALUE> +int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const { + int64_t totalCost = 0; + for (const auto& x : mClients) { + totalCost += x->getCost(); + } + return totalCost; +} + +// -------------------------------------------------------------------------------- + +}; // namespace resource_policy +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h new file mode 100644 index 0000000..df7c00e --- /dev/null +++ b/services/camera/libcameraservice/utils/RingBuffer.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 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. + */ + + +#ifndef ANDROID_SERVICE_UTILS_RING_BUFFER_H +#define ANDROID_SERVICE_UTILS_RING_BUFFER_H + +#include <utils/Log.h> +#include <cutils/compiler.h> + +#include <iterator> +#include <utility> +#include <vector> + +namespace android { + +/** + * A RingBuffer class that maintains an array of objects that can grow up to a certain size. + * Elements added to the RingBuffer are inserted in the logical front of the buffer, and + * invalidate all current iterators for that RingBuffer object. + */ +template <class T> +class RingBuffer final { +public: + + /** + * Construct a RingBuffer that can grow up to the given length. + */ + RingBuffer(size_t length); + + /** + * Forward iterator to this class. Implements an std:forward_iterator. + */ + class iterator : public std::iterator<std::forward_iterator_tag, T> { + public: + iterator(T* ptr, size_t size, size_t pos, size_t ctr); + + iterator& operator++(); + + iterator operator++(int); + + bool operator==(const iterator& rhs); + + bool operator!=(const iterator& rhs); + + T& operator*(); + + T* operator->(); + + private: + T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Constant forward iterator to this class. Implements an std:forward_iterator. + */ + class const_iterator : public std::iterator<std::forward_iterator_tag, T> { + public: + const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr); + + const_iterator& operator++(); + + const_iterator operator++(int); + + bool operator==(const const_iterator& rhs); + + bool operator!=(const const_iterator& rhs); + + const T& operator*(); + + const T* operator->(); + + private: + const T* mPtr; + size_t mSize; + size_t mPos; + size_t mCtr; + }; + + /** + * Adds item to the front of this RingBuffer. If the RingBuffer is at its maximum length, + * this will result in the last element being replaced (this is done using the element's + * assignment operator). + * + * All current iterators are invalidated. + */ + void add(const T& item); + + /** + * Moves item to the front of this RingBuffer. Following a call to this, item should no + * longer be used. If the RingBuffer is at its maximum length, this will result in the + * last element being replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + void add(T&& item); + + /** + * Construct item in-place in the front of this RingBuffer using the given arguments. If + * the RingBuffer is at its maximum length, this will result in the last element being + * replaced (this is done using the element's assignment operator). + * + * All current iterators are invalidated. + */ + template <class... Args> + void emplace(Args&&... args); + + /** + * Get an iterator to the front of this RingBuffer. + */ + iterator begin(); + + /** + * Get an iterator to the end of this RingBuffer. + */ + iterator end(); + + /** + * Get a const_iterator to the front of this RingBuffer. + */ + const_iterator begin() const; + + /** + * Get a const_iterator to the end of this RingBuffer. + */ + const_iterator end() const; + + /** + * Return a reference to the element at a given index. If the index is out of range for + * this ringbuffer, [0, size), the behavior for this is undefined. + */ + T& operator[](size_t index); + + /** + * Return a const reference to the element at a given index. If the index is out of range + * for this ringbuffer, [0, size), the behavior for this is undefined. + */ + const T& operator[](size_t index) const; + + /** + * Return the current size of this RingBuffer. + */ + size_t size() const; + + /** + * Remove all elements from this RingBuffer and set the size to 0. + */ + void clear(); + +private: + size_t mFrontIdx; + size_t mMaxBufferSize; + std::vector<T> mBuffer; +}; // class RingBuffer + + +template <class T> +RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {} + +template <class T> +RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) { + iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template <class T> +bool RingBuffer<T>::iterator::operator==(const iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +T& RingBuffer<T>::iterator::operator*() { + return *(mPtr + mPos); +} + +template <class T> +T* RingBuffer<T>::iterator::operator->() { + return mPtr + mPos; +} + +template <class T> +RingBuffer<T>::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) : + mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {} + +template <class T> +typename RingBuffer<T>::const_iterator& RingBuffer<T>::const_iterator::operator++() { + ++mCtr; + + if (CC_UNLIKELY(mCtr == mSize)) { + mPos = mSize; + return *this; + } + + mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1); + return *this; +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) { + const_iterator tmp{mPtr, mSize, mPos, mCtr}; + ++(*this); + return tmp; +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) { + return (mPtr + mPos) == (rhs.mPtr + rhs.mPos); +} + +template <class T> +bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) { + return (mPtr + mPos) != (rhs.mPtr + rhs.mPos); +} + +template <class T> +const T& RingBuffer<T>::const_iterator::operator*() { + return *(mPtr + mPos); +} + +template <class T> +const T* RingBuffer<T>::const_iterator::operator->() { + return mPtr + mPos; +} + +template <class T> +void RingBuffer<T>::add(const T& item) { + if (mBuffer.size() < mMaxBufferSize) { + mBuffer.push_back(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + mBuffer[mFrontIdx] = item; + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +void RingBuffer<T>::add(T&& item) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.push_back(std::forward<T>(item)); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = std::forward<T>(item); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +template <class... Args> +void RingBuffer<T>::emplace(Args&&... args) { + if (mBuffer.size() != mMaxBufferSize) { + mBuffer.emplace_back(std::forward<Args>(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); + return; + } + + // Only works for types with move assignment operator + mBuffer[mFrontIdx] = T(std::forward<Args>(args)...); + mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::begin() { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::iterator RingBuffer<T>::end() { + size_t s = mBuffer.size(); + return iterator(mBuffer.data(), s, s, s); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::begin() const { + size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1; + return const_iterator(mBuffer.data(), mBuffer.size(), + (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0); +} + +template <class T> +typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const { + size_t s = mBuffer.size(); + return const_iterator(mBuffer.data(), s, s, s); +} + +template <class T> +T& RingBuffer<T>::operator[](size_t index) { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template <class T> +const T& RingBuffer<T>::operator[](size_t index) const { + LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.", + index, mBuffer.size()); + size_t pos = (index >= mFrontIdx) ? + mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index; + return mBuffer[pos]; +} + +template <class T> +size_t RingBuffer<T>::size() const { + return mBuffer.size(); +} + +template <class T> +void RingBuffer<T>::clear() { + mBuffer.clear(); + mFrontIdx = 0; +} + +}; // namespace android + +#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H + + diff --git a/services/medialog/Android.mk b/services/medialog/Android.mk index 95f2fef..03438bf 100644 --- a/services/medialog/Android.mk +++ b/services/medialog/Android.mk @@ -10,4 +10,6 @@ LOCAL_MODULE:= libmedialogservice LOCAL_32_BIT_ONLY := true +LOCAL_C_INCLUDES := $(call include-path-for, audio-utils) + include $(BUILD_SHARED_LIBRARY) diff --git a/services/mediaresourcemanager/Android.mk b/services/mediaresourcemanager/Android.mk new file mode 100644 index 0000000..84218cf --- /dev/null +++ b/services/mediaresourcemanager/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := ResourceManagerService.cpp + +LOCAL_SHARED_LIBRARIES := libmedia libstagefright libbinder libutils liblog + +LOCAL_MODULE:= libresourcemanagerservice + +LOCAL_32_BIT_ONLY := true + +LOCAL_C_INCLUDES += \ + $(TOPDIR)frameworks/av/include + +include $(BUILD_SHARED_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp new file mode 100644 index 0000000..7296d47 --- /dev/null +++ b/services/mediaresourcemanager/ResourceManagerService.cpp @@ -0,0 +1,345 @@ +/* +** +** Copyright 2015, 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_NDEBUG 0 +#define LOG_TAG "ResourceManagerService" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include <dirent.h> +#include <media/stagefright/ProcessInfo.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> + +#include "ResourceManagerService.h" + +namespace android { + +template <typename T> +static String8 getString(const Vector<T> &items) { + String8 itemsStr; + for (size_t i = 0; i < items.size(); ++i) { + itemsStr.appendFormat("%s ", items[i].toString().string()); + } + return itemsStr; +} + +static bool hasResourceType(String8 type, Vector<MediaResource> resources) { + for (size_t i = 0; i < resources.size(); ++i) { + if (resources[i].mType == type) { + return true; + } + } + return false; +} + +static bool hasResourceType(String8 type, ResourceInfos infos) { + for (size_t i = 0; i < infos.size(); ++i) { + if (hasResourceType(type, infos[i].resources)) { + return true; + } + } + return false; +} + +static ResourceInfos& getResourceInfosForEdit( + int pid, + PidResourceInfosMap& map) { + ssize_t index = map.indexOfKey(pid); + if (index < 0) { + // new pid + ResourceInfos infosForPid; + map.add(pid, infosForPid); + } + + return map.editValueFor(pid); +} + +static ResourceInfo& getResourceInfoForEdit( + int64_t clientId, + const sp<IResourceManagerClient> client, + ResourceInfos& infos) { + for (size_t i = 0; i < infos.size(); ++i) { + if (infos[i].clientId == clientId) { + return infos.editItemAt(i); + } + } + ResourceInfo info; + info.clientId = clientId; + info.client = client; + infos.push_back(info); + return infos.editItemAt(infos.size() - 1); +} + +ResourceManagerService::ResourceManagerService() + : mProcessInfo(new ProcessInfo()), + mSupportsMultipleSecureCodecs(true), + mSupportsSecureWithNonSecureCodec(true) {} + +ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo) + : mProcessInfo(processInfo), + mSupportsMultipleSecureCodecs(true), + mSupportsSecureWithNonSecureCodec(true) {} + +ResourceManagerService::~ResourceManagerService() {} + +void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) { + ALOGV("config(%s)", getString(policies).string()); + + Mutex::Autolock lock(mLock); + for (size_t i = 0; i < policies.size(); ++i) { + String8 type = policies[i].mType; + uint64_t value = policies[i].mValue; + if (type == kPolicySupportsMultipleSecureCodecs) { + mSupportsMultipleSecureCodecs = (value != 0); + } else if (type == kPolicySupportsSecureWithNonSecureCodec) { + mSupportsSecureWithNonSecureCodec = (value != 0); + } + } +} + +void ResourceManagerService::addResource( + int pid, + int64_t clientId, + const sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources) { + ALOGV("addResource(pid %d, clientId %lld, resources %s)", + pid, (long long) clientId, getString(resources).string()); + + Mutex::Autolock lock(mLock); + ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); + ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos); + info.resources.appendVector(resources); +} + +void ResourceManagerService::removeResource(int64_t clientId) { + ALOGV("removeResource(%lld)", (long long) clientId); + + Mutex::Autolock lock(mLock); + bool found = false; + for (size_t i = 0; i < mMap.size(); ++i) { + ResourceInfos &infos = mMap.editValueAt(i); + for (size_t j = 0; j < infos.size();) { + if (infos[j].clientId == clientId) { + j = infos.removeAt(j); + found = true; + } else { + ++j; + } + } + if (found) { + break; + } + } + if (!found) { + ALOGV("didn't find client"); + } +} + +bool ResourceManagerService::reclaimResource( + int callingPid, const Vector<MediaResource> &resources) { + ALOGV("reclaimResource(callingPid %d, resources %s)", + callingPid, getString(resources).string()); + + Vector<sp<IResourceManagerClient>> clients; + { + Mutex::Autolock lock(mLock); + // first pass to handle secure/non-secure codec conflict + for (size_t i = 0; i < resources.size(); ++i) { + String8 type = resources[i].mType; + if (type == kResourceSecureCodec) { + if (!mSupportsMultipleSecureCodecs) { + if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) { + return false; + } + } + if (!mSupportsSecureWithNonSecureCodec) { + if (!getAllClients_l(callingPid, String8(kResourceNonSecureCodec), &clients)) { + return false; + } + } + } else if (type == kResourceNonSecureCodec) { + if (!mSupportsSecureWithNonSecureCodec) { + if (!getAllClients_l(callingPid, String8(kResourceSecureCodec), &clients)) { + return false; + } + } + } + } + + if (clients.size() == 0) { + // if no secure/non-secure codec conflict, run second pass to handle other resources. + for (size_t i = 0; i < resources.size(); ++i) { + String8 type = resources[i].mType; + if (type == kResourceGraphicMemory) { + sp<IResourceManagerClient> client; + if (!getLowestPriorityBiggestClient_l(callingPid, type, &client)) { + return false; + } + clients.push_back(client); + } + } + } + } + + if (clients.size() == 0) { + return false; + } + + for (size_t i = 0; i < clients.size(); ++i) { + ALOGV("reclaimResource from client %p", clients[i].get()); + if (!clients[i]->reclaimResource()) { + return false; + } + } + return true; +} + +bool ResourceManagerService::getAllClients_l( + int callingPid, const String8 &type, Vector<sp<IResourceManagerClient>> *clients) { + Vector<sp<IResourceManagerClient>> temp; + for (size_t i = 0; i < mMap.size(); ++i) { + ResourceInfos &infos = mMap.editValueAt(i); + for (size_t j = 0; j < infos.size(); ++j) { + if (hasResourceType(type, infos[j].resources)) { + if (!isCallingPriorityHigher_l(callingPid, mMap.keyAt(i))) { + // some higher/equal priority process owns the resource, + // this request can't be fulfilled. + ALOGE("getAllClients_l: can't reclaim resource %s from pid %d", + type.string(), mMap.keyAt(i)); + return false; + } + temp.push_back(infos[j].client); + } + } + } + if (temp.size() == 0) { + ALOGV("getAllClients_l: didn't find any resource %s", type.string()); + return true; + } + clients->appendVector(temp); + return true; +} + +bool ResourceManagerService::getLowestPriorityBiggestClient_l( + int callingPid, const String8 &type, sp<IResourceManagerClient> *client) { + int lowestPriorityPid; + int lowestPriority; + int callingPriority; + if (!mProcessInfo->getPriority(callingPid, &callingPriority)) { + ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d", + callingPid); + return false; + } + if (!getLowestPriorityPid_l(type, &lowestPriorityPid, &lowestPriority)) { + return false; + } + if (lowestPriority <= callingPriority) { + ALOGE("getLowestPriorityBiggestClient_l: lowest priority %d vs caller priority %d", + lowestPriority, callingPriority); + return false; + } + + if (!getBiggestClient_l(lowestPriorityPid, type, client)) { + return false; + } + return true; +} + +bool ResourceManagerService::getLowestPriorityPid_l( + const String8 &type, int *lowestPriorityPid, int *lowestPriority) { + int pid = -1; + int priority = -1; + for (size_t i = 0; i < mMap.size(); ++i) { + if (mMap.valueAt(i).size() == 0) { + // no client on this process. + continue; + } + if (!hasResourceType(type, mMap.valueAt(i))) { + // doesn't have the requested resource type + continue; + } + int tempPid = mMap.keyAt(i); + int tempPriority; + if (!mProcessInfo->getPriority(tempPid, &tempPriority)) { + ALOGV("getLowestPriorityPid_l: can't get priority of pid %d, skipped", tempPid); + // TODO: remove this pid from mMap? + continue; + } + if (pid == -1 || tempPriority > priority) { + // initial the value + pid = tempPid; + priority = tempPriority; + } + } + if (pid != -1) { + *lowestPriorityPid = pid; + *lowestPriority = priority; + } + return (pid != -1); +} + +bool ResourceManagerService::isCallingPriorityHigher_l(int callingPid, int pid) { + int callingPidPriority; + if (!mProcessInfo->getPriority(callingPid, &callingPidPriority)) { + return false; + } + + int priority; + if (!mProcessInfo->getPriority(pid, &priority)) { + return false; + } + + return (callingPidPriority < priority); +} + +bool ResourceManagerService::getBiggestClient_l( + int pid, const String8 &type, sp<IResourceManagerClient> *client) { + ssize_t index = mMap.indexOfKey(pid); + if (index < 0) { + ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid); + return false; + } + + sp<IResourceManagerClient> clientTemp; + uint64_t largestValue = 0; + const ResourceInfos &infos = mMap.valueAt(index); + for (size_t i = 0; i < infos.size(); ++i) { + Vector<MediaResource> resources = infos[i].resources; + for (size_t j = 0; j < resources.size(); ++j) { + if (resources[j].mType == type) { + if (resources[j].mValue > largestValue) { + largestValue = resources[j].mValue; + clientTemp = infos[i].client; + } + } + } + } + + if (clientTemp == NULL) { + ALOGE("getBiggestClient_l: can't find resource type %s for pid %d", type.string(), pid); + return false; + } + + *client = clientTemp; + return true; +} + +} // namespace android diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h new file mode 100644 index 0000000..2ed9bf8 --- /dev/null +++ b/services/mediaresourcemanager/ResourceManagerService.h @@ -0,0 +1,106 @@ +/* +** +** Copyright 2015, 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. +*/ + +#ifndef ANDROID_RESOURCEMANAGERSERVICE_H +#define ANDROID_RESOURCEMANAGERSERVICE_H + +#include <arpa/inet.h> +#include <binder/BinderService.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/threads.h> +#include <utils/Vector.h> + +#include <media/IResourceManagerService.h> + +namespace android { + +struct ProcessInfoInterface; + +struct ResourceInfo { + int64_t clientId; + sp<IResourceManagerClient> client; + Vector<MediaResource> resources; +}; + +typedef Vector<ResourceInfo> ResourceInfos; +typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap; + +class ResourceManagerService + : public BinderService<ResourceManagerService>, + public BnResourceManagerService +{ +public: + static char const *getServiceName() { return "media.resource_manager"; } + + ResourceManagerService(); + ResourceManagerService(sp<ProcessInfoInterface> processInfo); + + // IResourceManagerService interface + virtual void config(const Vector<MediaResourcePolicy> &policies); + + virtual void addResource( + int pid, + int64_t clientId, + const sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources); + + virtual void removeResource(int64_t clientId); + + virtual bool reclaimResource(int callingPid, const Vector<MediaResource> &resources); + +protected: + virtual ~ResourceManagerService(); + +private: + friend class ResourceManagerServiceTest; + + // Gets the list of all the clients who own the specified resource type. + // Returns false if any client belongs to a process with higher priority than the + // calling process. The clients will remain unchanged if returns false. + bool getAllClients_l(int callingPid, const String8 &type, + Vector<sp<IResourceManagerClient>> *clients); + + // Gets the client who owns specified resource type from lowest possible priority process. + // Returns false if the calling process priority is not higher than the lowest process + // priority. The client will remain unchanged if returns false. + bool getLowestPriorityBiggestClient_l(int callingPid, const String8 &type, + sp<IResourceManagerClient> *client); + + // Gets lowest priority process that has the specified resource type. + // Returns false if failed. The output parameters will remain unchanged if failed. + bool getLowestPriorityPid_l(const String8 &type, int *pid, int *priority); + + // Gets the client who owns biggest piece of specified resource type from pid. + // Returns false if failed. The client will remain unchanged if failed. + bool getBiggestClient_l(int pid, const String8 &type, sp<IResourceManagerClient> *client); + + bool isCallingPriorityHigher_l(int callingPid, int pid); + + mutable Mutex mLock; + sp<ProcessInfoInterface> mProcessInfo; + PidResourceInfosMap mMap; + bool mSupportsMultipleSecureCodecs; + bool mSupportsSecureWithNonSecureCodec; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_RESOURCEMANAGERSERVICE_H diff --git a/services/mediaresourcemanager/test/Android.mk b/services/mediaresourcemanager/test/Android.mk new file mode 100644 index 0000000..228b62a --- /dev/null +++ b/services/mediaresourcemanager/test/Android.mk @@ -0,0 +1,25 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := ResourceManagerService_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + ResourceManagerService_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + liblog \ + libmedia \ + libresourcemanagerservice \ + libutils \ + +LOCAL_C_INCLUDES := \ + frameworks/av/include \ + frameworks/av/services/mediaresourcemanager \ + +LOCAL_32_BIT_ONLY := true + +include $(BUILD_NATIVE_TEST) diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp new file mode 100644 index 0000000..b73e1bc --- /dev/null +++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2015 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_NDEBUG 0 +#define LOG_TAG "ResourceManagerService_test" +#include <utils/Log.h> + +#include <gtest/gtest.h> + +#include "ResourceManagerService.h" +#include <media/IResourceManagerService.h> +#include <media/MediaResource.h> +#include <media/MediaResourcePolicy.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/ProcessInfoInterface.h> + +namespace android { + +struct TestProcessInfo : public ProcessInfoInterface { + TestProcessInfo() {} + virtual ~TestProcessInfo() {} + + virtual bool getPriority(int pid, int *priority) { + // For testing, use pid as priority. + // Lower the value higher the priority. + *priority = pid; + return true; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo); +}; + +struct TestClient : public BnResourceManagerClient { + TestClient(sp<ResourceManagerService> service) + : mReclaimed(false), mService(service) {} + + virtual bool reclaimResource() { + sp<IResourceManagerClient> client(this); + mService->removeResource((int64_t) client.get()); + mReclaimed = true; + return true; + } + + bool reclaimed() const { + return mReclaimed; + } + + void reset() { + mReclaimed = false; + } + +protected: + virtual ~TestClient() {} + +private: + bool mReclaimed; + sp<ResourceManagerService> mService; + DISALLOW_EVIL_CONSTRUCTORS(TestClient); +}; + +static const int kTestPid1 = 30; +static const int kTestPid2 = 20; + +class ResourceManagerServiceTest : public ::testing::Test { +public: + ResourceManagerServiceTest() + : mService(new ResourceManagerService(new TestProcessInfo)), + mTestClient1(new TestClient(mService)), + mTestClient2(new TestClient(mService)), + mTestClient3(new TestClient(mService)) { + } + +protected: + static bool isEqualResources(const Vector<MediaResource> &resources1, + const Vector<MediaResource> &resources2) { + if (resources1.size() != resources2.size()) { + return false; + } + for (size_t i = 0; i < resources1.size(); ++i) { + if (resources1[i] != resources2[i]) { + return false; + } + } + return true; + } + + static void expectEqResourceInfo(const ResourceInfo &info, sp<IResourceManagerClient> client, + const Vector<MediaResource> &resources) { + EXPECT_EQ(client, info.client); + EXPECT_TRUE(isEqualResources(resources, info.resources)); + } + + void verifyClients(bool c1, bool c2, bool c3) { + TestClient *client1 = static_cast<TestClient*>(mTestClient1.get()); + TestClient *client2 = static_cast<TestClient*>(mTestClient2.get()); + TestClient *client3 = static_cast<TestClient*>(mTestClient3.get()); + + EXPECT_EQ(c1, client1->reclaimed()); + EXPECT_EQ(c2, client2->reclaimed()); + EXPECT_EQ(c3, client3->reclaimed()); + + client1->reset(); + client2->reset(); + client3->reset(); + } + + void addResource() { + // kTestPid1 mTestClient1 + Vector<MediaResource> resources1; + resources1.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + mService->addResource(kTestPid1, (int64_t) mTestClient1.get(), mTestClient1, resources1); + resources1.push_back(MediaResource(String8(kResourceGraphicMemory), 200)); + Vector<MediaResource> resources11; + resources11.push_back(MediaResource(String8(kResourceGraphicMemory), 200)); + mService->addResource(kTestPid1, (int64_t) mTestClient1.get(), mTestClient1, resources11); + + // kTestPid2 mTestClient2 + Vector<MediaResource> resources2; + resources2.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); + resources2.push_back(MediaResource(String8(kResourceGraphicMemory), 300)); + mService->addResource(kTestPid2, (int64_t) mTestClient2.get(), mTestClient2, resources2); + + // kTestPid2 mTestClient3 + Vector<MediaResource> resources3; + mService->addResource(kTestPid2, (int64_t) mTestClient3.get(), mTestClient3, resources3); + resources3.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + resources3.push_back(MediaResource(String8(kResourceGraphicMemory), 100)); + mService->addResource(kTestPid2, (int64_t) mTestClient3.get(), mTestClient3, resources3); + + const PidResourceInfosMap &map = mService->mMap; + EXPECT_EQ(2u, map.size()); + ssize_t index1 = map.indexOfKey(kTestPid1); + ASSERT_GE(index1, 0); + const ResourceInfos &infos1 = map[index1]; + EXPECT_EQ(1u, infos1.size()); + expectEqResourceInfo(infos1[0], mTestClient1, resources1); + + ssize_t index2 = map.indexOfKey(kTestPid2); + ASSERT_GE(index2, 0); + const ResourceInfos &infos2 = map[index2]; + EXPECT_EQ(2u, infos2.size()); + expectEqResourceInfo(infos2[0], mTestClient2, resources2); + expectEqResourceInfo(infos2[1], mTestClient3, resources3); + } + + void testConfig() { + EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs); + EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); + + Vector<MediaResourcePolicy> policies1; + policies1.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 1)); + policies1.push_back( + MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 0)); + mService->config(policies1); + EXPECT_TRUE(mService->mSupportsMultipleSecureCodecs); + EXPECT_FALSE(mService->mSupportsSecureWithNonSecureCodec); + + Vector<MediaResourcePolicy> policies2; + policies2.push_back(MediaResourcePolicy(String8(kPolicySupportsMultipleSecureCodecs), 0)); + policies2.push_back( + MediaResourcePolicy(String8(kPolicySupportsSecureWithNonSecureCodec), 1)); + mService->config(policies2); + EXPECT_FALSE(mService->mSupportsMultipleSecureCodecs); + EXPECT_TRUE(mService->mSupportsSecureWithNonSecureCodec); + } + + void testRemoveResource() { + addResource(); + + mService->removeResource((int64_t) mTestClient2.get()); + + const PidResourceInfosMap &map = mService->mMap; + EXPECT_EQ(2u, map.size()); + const ResourceInfos &infos1 = map.valueFor(kTestPid1); + const ResourceInfos &infos2 = map.valueFor(kTestPid2); + EXPECT_EQ(1u, infos1.size()); + EXPECT_EQ(1u, infos2.size()); + // mTestClient2 has been removed. + EXPECT_EQ(mTestClient3, infos2[0].client); + } + + void testGetAllClients() { + addResource(); + + String8 type = String8(kResourceSecureCodec); + String8 unknowType = String8("unknowType"); + Vector<sp<IResourceManagerClient> > clients; + int lowPriorityPid = 100; + EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); + int midPriorityPid = 25; + EXPECT_FALSE(mService->getAllClients_l(lowPriorityPid, type, &clients)); + int highPriorityPid = 10; + EXPECT_TRUE(mService->getAllClients_l(10, unknowType, &clients)); + EXPECT_TRUE(mService->getAllClients_l(10, type, &clients)); + + EXPECT_EQ(2u, clients.size()); + EXPECT_EQ(mTestClient3, clients[0]); + EXPECT_EQ(mTestClient1, clients[1]); + } + + void testReclaimResourceSecure() { + Vector<MediaResource> resources; + resources.push_back(MediaResource(String8(kResourceSecureCodec), 1)); + resources.push_back(MediaResource(String8(kResourceGraphicMemory), 150)); + + // ### secure codec can't coexist and secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = false; + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, true); + + // call again should reclaim one largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + // ### secure codecs can't coexist and secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = false; + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure and non-secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, true, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + + // ### secure codecs can coexist but secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = true; + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all non-secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim one largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, false); + + // call again should reclaim another largest graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + // ### secure codecs can coexist and secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsMultipleSecureCodecs = true; + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + + EXPECT_TRUE(mService->reclaimResource(10, resources)); + // one largest graphic memory from lowest process got reclaimed + verifyClients(true, false, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + } + + void testReclaimResourceNonSecure() { + Vector<MediaResource> resources; + resources.push_back(MediaResource(String8(kResourceNonSecureCodec), 1)); + resources.push_back(MediaResource(String8(kResourceGraphicMemory), 150)); + + // ### secure codec can't coexist with non-secure codec ### + { + addResource(); + mService->mSupportsSecureWithNonSecureCodec = false; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + EXPECT_FALSE(mService->reclaimResource(25, resources)); + + // reclaim all secure codecs + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(true, false, true); + + // call again should reclaim one graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + + + // ### secure codec can coexist with non-secure codec ### + { + addResource(); + mService->mSupportsSecureWithNonSecureCodec = true; + + // priority too low + EXPECT_FALSE(mService->reclaimResource(40, resources)); + + EXPECT_TRUE(mService->reclaimResource(10, resources)); + // one largest graphic memory from lowest process got reclaimed + verifyClients(true, false, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, true, false); + + // call again should reclaim another graphic memory from lowest process + EXPECT_TRUE(mService->reclaimResource(10, resources)); + verifyClients(false, false, true); + + // nothing left + EXPECT_FALSE(mService->reclaimResource(10, resources)); + } + } + + void testGetLowestPriorityBiggestClient() { + String8 type = String8(kResourceGraphicMemory); + sp<IResourceManagerClient> client; + EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(10, type, &client)); + + addResource(); + + EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(100, type, &client)); + EXPECT_TRUE(mService->getLowestPriorityBiggestClient_l(10, type, &client)); + + // kTestPid1 is the lowest priority process with kResourceGraphicMemory. + // mTestClient1 has the largest kResourceGraphicMemory within kTestPid1. + EXPECT_EQ(mTestClient1, client); + } + + void testGetLowestPriorityPid() { + int pid; + int priority; + TestProcessInfo processInfo; + + String8 type = String8(kResourceGraphicMemory); + EXPECT_FALSE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + + addResource(); + + EXPECT_TRUE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + EXPECT_EQ(kTestPid1, pid); + int priority1; + processInfo.getPriority(kTestPid1, &priority1); + EXPECT_EQ(priority1, priority); + + type = String8(kResourceNonSecureCodec); + EXPECT_TRUE(mService->getLowestPriorityPid_l(type, &pid, &priority)); + EXPECT_EQ(kTestPid2, pid); + int priority2; + processInfo.getPriority(kTestPid2, &priority2); + EXPECT_EQ(priority2, priority); + } + + void testGetBiggestClient() { + String8 type = String8(kResourceGraphicMemory); + sp<IResourceManagerClient> client; + EXPECT_FALSE(mService->getBiggestClient_l(kTestPid2, type, &client)); + + addResource(); + + EXPECT_TRUE(mService->getBiggestClient_l(kTestPid2, type, &client)); + EXPECT_EQ(mTestClient2, client); + } + + void testIsCallingPriorityHigher() { + EXPECT_FALSE(mService->isCallingPriorityHigher_l(101, 100)); + EXPECT_FALSE(mService->isCallingPriorityHigher_l(100, 100)); + EXPECT_TRUE(mService->isCallingPriorityHigher_l(99, 100)); + } + + sp<ResourceManagerService> mService; + sp<IResourceManagerClient> mTestClient1; + sp<IResourceManagerClient> mTestClient2; + sp<IResourceManagerClient> mTestClient3; +}; + +TEST_F(ResourceManagerServiceTest, config) { + testConfig(); +} + +TEST_F(ResourceManagerServiceTest, addResource) { + addResource(); +} + +TEST_F(ResourceManagerServiceTest, removeResource) { + testRemoveResource(); +} + +TEST_F(ResourceManagerServiceTest, reclaimResource) { + testReclaimResourceSecure(); + testReclaimResourceNonSecure(); +} + +TEST_F(ResourceManagerServiceTest, getAllClients_l) { + testGetAllClients(); +} + +TEST_F(ResourceManagerServiceTest, getLowestPriorityBiggestClient_l) { + testGetLowestPriorityBiggestClient(); +} + +TEST_F(ResourceManagerServiceTest, getLowestPriorityPid_l) { + testGetLowestPriorityPid(); +} + +TEST_F(ResourceManagerServiceTest, getBiggestClient_l) { + testGetBiggestClient(); +} + +TEST_F(ResourceManagerServiceTest, isCallingPriorityHigher_l) { + testIsCallingPriorityHigher(); +} + +} // namespace android diff --git a/services/radio/Android.mk b/services/radio/Android.mk new file mode 100644 index 0000000..9ee5666 --- /dev/null +++ b/services/radio/Android.mk @@ -0,0 +1,36 @@ +# Copyright 2014 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +LOCAL_SRC_FILES:= \ + RadioService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libmedia \ + libhardware \ + libradio \ + libradio_metadata + +LOCAL_MODULE:= libradioservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/radio/RadioRegions.h b/services/radio/RadioRegions.h new file mode 100644 index 0000000..3335b8a --- /dev/null +++ b/services/radio/RadioRegions.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_REGIONS_H +#define ANDROID_HARDWARE_RADIO_REGIONS_H + +namespace android { + +#define RADIO_BAND_LOWER_FM_ITU1 87500 +#define RADIO_BAND_UPPER_FM_ITU1 108000 +#define RADIO_BAND_SPACING_FM_ITU1 100 + +#define RADIO_BAND_LOWER_FM_ITU2 87900 +#define RADIO_BAND_UPPER_FM_ITU2 107900 +#define RADIO_BAND_SPACING_FM_ITU2 200 + +#define RADIO_BAND_LOWER_FM_JAPAN 76000 +#define RADIO_BAND_UPPER_FM_JAPAN 90000 +#define RADIO_BAND_SPACING_FM_JAPAN 100 + +#define RADIO_BAND_LOWER_FM_OIRT 65800 +#define RADIO_BAND_UPPER_FM_OIRT 74000 +#define RADIO_BAND_SPACING_FM_OIRT 10 + +#define RADIO_BAND_LOWER_LW 153 +#define RADIO_BAND_UPPER_LW 279 +#define RADIO_BAND_SPACING_LW 9 + +#define RADIO_BAND_LOWER_MW_IUT1 531 +#define RADIO_BAND_UPPER_MW_ITU1 1611 +#define RADIO_BAND_SPACING_MW_ITU1 9 + +#define RADIO_BAND_LOWER_MW_IUT2 540 +#define RADIO_BAND_UPPER_MW_ITU2 1610 +#define RADIO_BAND_SPACING_MW_ITU2 10 + +#define RADIO_BAND_LOWER_SW 2300 +#define RADIO_BAND_UPPER_SW 26100 +#define RADIO_BAND_SPACING_SW 5 + + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +const radio_band_config_t sKnownRegionConfigs[] = { + { // FM ITU 1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Americas + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // FM Japan + RADIO_REGION_JAPAN, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_JAPAN, + RADIO_BAND_UPPER_FM_JAPAN, + 1, + {RADIO_BAND_SPACING_FM_JAPAN}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM Korea + RADIO_REGION_KOREA, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_ITU1, + RADIO_BAND_UPPER_FM_ITU1, + 1, + {RADIO_BAND_SPACING_FM_ITU1}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM OIRT + RADIO_REGION_OIRT, + { + RADIO_BAND_FM, + false, + RADIO_BAND_LOWER_FM_OIRT, + RADIO_BAND_UPPER_FM_OIRT, + 1, + {RADIO_BAND_SPACING_FM_OIRT}, + { + RADIO_DEEMPHASIS_50, + true, + RADIO_RDS_WORLD, + true, + true, + } + } + }, + { // FM US HD radio + RADIO_REGION_ITU_2, + { + RADIO_BAND_FM_HD, + false, + RADIO_BAND_LOWER_FM_ITU2, + RADIO_BAND_UPPER_FM_ITU2, + 1, + {RADIO_BAND_SPACING_FM_ITU2}, + { + RADIO_DEEMPHASIS_75, + true, + RADIO_RDS_US, + true, + true, + } + } + }, + { // AM LW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_LW, + RADIO_BAND_UPPER_LW, + 1, + {RADIO_BAND_SPACING_LW}, + { + } + } + }, + { // AM SW + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_SW, + RADIO_BAND_UPPER_SW, + 1, + {RADIO_BAND_SPACING_SW}, + { + } + } + }, + { // AM MW ITU1 + RADIO_REGION_ITU_1, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT1, + RADIO_BAND_UPPER_MW_ITU1, + 1, + {RADIO_BAND_SPACING_MW_ITU1}, + { + } + } + }, + { // AM MW ITU2 + RADIO_REGION_ITU_2, + { + RADIO_BAND_AM, + false, + RADIO_BAND_LOWER_MW_IUT2, + RADIO_BAND_UPPER_MW_ITU2, + 1, + {RADIO_BAND_SPACING_MW_ITU2}, + { + } + } + } +}; + + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_REGIONS_H diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp new file mode 100644 index 0000000..a6c2bdf --- /dev/null +++ b/services/radio/RadioService.cpp @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2015 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 "RadioService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <system/radio.h> +#include <system/radio_metadata.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <hardware/radio.h> +#include <media/AudioSystem.h> +#include "RadioService.h" +#include "RadioRegions.h" + +namespace android { + +static const char kRadioTunerAudioDeviceName[] = "Radio tuner source"; + +RadioService::RadioService() + : BnRadioService(), mNextUniqueId(1) +{ + ALOGI("%s", __FUNCTION__); +} + +void RadioService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + struct radio_hw_device *dev; + + ALOGI("%s", __FUNCTION__); + + rc = hw_get_module_by_class(RADIO_HARDWARE_MODULE_ID, RADIO_HARDWARE_MODULE_ID_FM, &mod); + if (rc != 0) { + ALOGE("couldn't load radio module %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = radio_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open radio hw device in %s.%s (%s)", + RADIO_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != RADIO_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong radio hw device version %04x", dev->common.version); + return; + } + + struct radio_hal_properties halProperties; + rc = dev->get_properties(dev, &halProperties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + + radio_properties_t properties; + properties.handle = + (radio_handle_t)android_atomic_inc(&mNextUniqueId); + + ALOGI("loaded default module %s, handle %d", properties.product, properties.handle); + + convertProperties(&properties, &halProperties); + sp<Module> module = new Module(dev, properties); + mModules.add(properties.handle, module); +} + +RadioService::~RadioService() +{ + for (size_t i = 0; i < mModules.size(); i++) { + radio_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t RadioService::listModules(struct radio_properties *properties, + uint32_t *numModules) +{ + ALOGV("listModules"); + + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && properties == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + properties[i] = mModules.valueAt(i)->properties(); + } + return NO_ERROR; +} + +status_t RadioService::attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio) +{ + ALOGV("%s %d config %p withAudio %d", __FUNCTION__, handle, config, withAudio); + + AutoMutex lock(mServiceLock); + radio.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + if (config == NULL) { + config = module->getDefaultConfig(); + if (config == NULL) { + return INVALID_OPERATION; + } + } + ALOGV("%s region %d type %d", __FUNCTION__, config->region, config->band.type); + + radio = module->addClient(client, config, withAudio); + + if (radio == 0) { + NO_INIT; + } + return NO_ERROR; +} + + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t RadioService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump RadioService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - RadioService is probably deadlocked + if (!locked) { + result.append("RadioService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t RadioService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnRadioService::onTransact(code, data, reply, flags); +} + + +// static +void RadioService::callback(radio_hal_event_t *halEvent, void *cookie) +{ + CallbackThread *callbackThread = (CallbackThread *)cookie; + if (callbackThread == NULL) { + return; + } + callbackThread->sendEvent(halEvent); +} + +/* static */ +void RadioService::convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties) +{ + memset(properties, 0, sizeof(struct radio_properties)); + properties->class_id = halProperties->class_id; + strlcpy(properties->implementor, halProperties->implementor, + RADIO_STRING_LEN_MAX); + strlcpy(properties->product, halProperties->product, + RADIO_STRING_LEN_MAX); + strlcpy(properties->version, halProperties->version, + RADIO_STRING_LEN_MAX); + strlcpy(properties->serial, halProperties->serial, + RADIO_STRING_LEN_MAX); + properties->num_tuners = halProperties->num_tuners; + properties->num_audio_sources = halProperties->num_audio_sources; + properties->supports_capture = halProperties->supports_capture; + + for (size_t i = 0; i < ARRAY_SIZE(sKnownRegionConfigs); i++) { + const radio_hal_band_config_t *band = &sKnownRegionConfigs[i].band; + size_t j; + for (j = 0; j < halProperties->num_bands; j++) { + const radio_hal_band_config_t *halBand = &halProperties->bands[j]; + size_t k; + if (band->type != halBand->type) continue; + if (band->lower_limit < halBand->lower_limit) continue; + if (band->upper_limit > halBand->upper_limit) continue; + for (k = 0; k < halBand->num_spacings; k++) { + if (band->spacings[0] == halBand->spacings[k]) break; + } + if (k == halBand->num_spacings) continue; + if (band->type == RADIO_BAND_AM) break; + if ((band->fm.deemphasis & halBand->fm.deemphasis) == 0) continue; + if (halBand->fm.rds == 0) break; + if ((band->fm.rds & halBand->fm.rds) != 0) break; + } + if (j == halProperties->num_bands) continue; + + ALOGI("convertProperties() Adding band type %d region %d", + sKnownRegionConfigs[i].band.type , sKnownRegionConfigs[i].region); + + memcpy(&properties->bands[properties->num_bands++], + &sKnownRegionConfigs[i], + sizeof(radio_band_config_t)); + } +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::CallbackThread" + +RadioService::CallbackThread::CallbackThread(const wp<ModuleClient>& moduleClient) + : mModuleClient(moduleClient), mMemoryDealer(new MemoryDealer(1024 * 1024, "RadioService")) +{ +} + +RadioService::CallbackThread::~CallbackThread() +{ + mEventQueue.clear(); +} + +void RadioService::CallbackThread::onFirstRef() +{ + run("RadioService cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool RadioService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<IMemory> eventMemory; + sp<ModuleClient> moduleClient; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + eventMemory = mEventQueue[0]; + mEventQueue.removeAt(0); + moduleClient = mModuleClient.promote(); + } + if (moduleClient != 0) { + moduleClient->onCallbackEvent(eventMemory); + eventMemory.clear(); + } + } + return false; +} + +void RadioService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +sp<IMemory> RadioService::CallbackThread::prepareEvent(radio_hal_event_t *halEvent) +{ + sp<IMemory> eventMemory; + + size_t headerSize = + (sizeof(struct radio_event) + sizeof(unsigned int) - 1) /sizeof(unsigned int); + size_t metadataSize = 0; + switch (halEvent->type) { + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + if (radio_metadata_check(halEvent->info.metadata) == 0) { + metadataSize = radio_metadata_get_size(halEvent->info.metadata); + } + break; + case RADIO_EVENT_METADATA: + if (radio_metadata_check(halEvent->metadata) != 0) { + return eventMemory; + } + metadataSize = radio_metadata_get_size(halEvent->metadata); + break; + default: + break; + } + size_t size = headerSize + metadataSize; + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + struct radio_event *event = (struct radio_event *)eventMemory->pointer(); + event->type = halEvent->type; + event->status = halEvent->status; + + switch (event->type) { + case RADIO_EVENT_CONFIG: + event->config.band = halEvent->config; + break; + case RADIO_EVENT_TUNED: + case RADIO_EVENT_AF_SWITCH: + event->info = halEvent->info; + if (metadataSize != 0) { + memcpy((char *)event + headerSize, halEvent->info.metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->info.metadata = (radio_metadata_t *)headerSize; + } + break; + case RADIO_EVENT_TA: + case RADIO_EVENT_ANTENNA: + case RADIO_EVENT_CONTROL: + event->on = halEvent->on; + break; + case RADIO_EVENT_METADATA: + memcpy((char *)event + headerSize, halEvent->metadata, metadataSize); + // replace meta data pointer by offset while in shared memory so that receiving side + // can restore the pointer in destination process. + event->metadata = (radio_metadata_t *)headerSize; + break; + case RADIO_EVENT_HW_FAILURE: + default: + break; + } + + return eventMemory; +} + +void RadioService::CallbackThread::sendEvent(radio_hal_event_t *event) + { + sp<IMemory> eventMemory = prepareEvent(event); + if (eventMemory == 0) { + return; + } + + AutoMutex lock(mCallbackLock); + mEventQueue.add(eventMemory); + mCallbackCond.signal(); + ALOGV("%s DONE", __FUNCTION__); +} + + +#undef LOG_TAG +#define LOG_TAG "RadioService::Module" + +RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties) + : mHwDevice(hwDevice), mProperties(properties), mMute(true) +{ +} + +RadioService::Module::~Module() { + mModuleClients.clear(); +} + +status_t RadioService::Module::dump(int fd __unused, const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +sp<RadioService::ModuleClient> RadioService::Module::addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) +{ + ALOGV("addClient() %p config %p product %s", this, config, mProperties.product); + AutoMutex lock(mLock); + sp<ModuleClient> moduleClient; + int ret; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->client() == client) { + // client already connected: reject + return moduleClient; + } + } + moduleClient = new ModuleClient(this, client, config, audio); + + struct radio_hal_band_config halConfig; + halConfig = config->band; + + // Tuner preemption logic: + // There is a limited amount of tuners and a limited amount of radio audio sources per module. + // The minimum is one tuner and one audio source. + // The numbers of tuners and sources are indicated in the module properties. + // NOTE: current framework implementation only supports one radio audio source. + // It is possible to open more than one tuner at a time but only one tuner can be connected + // to the radio audio source (AUDIO_DEVICE_IN_FM_TUNER). + // The base rule is that a newly connected tuner always wins, i.e. always gets a tuner + // and can use the audio source if requested. + // If another client is preempted, it is notified by a callback with RADIO_EVENT_CONTROL + // indicating loss of control. + // - If the newly connected client requests the audio source (audio == true): + // - if an audio source is available + // no problem + // - if not: + // the oldest client in the list using audio is preempted. + // - If the newly connected client does not request the audio source (audio == false): + // - if a tuner is available + // no problem + // - if not: + // The oldest client not using audio is preempted first and if none is found the + // the oldest client using audio is preempted. + // Each time a tuner using the audio source is opened or closed, the audio policy manager is + // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER. + + sp<ModuleClient> oldestTuner; + sp<ModuleClient> oldestAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i]->getTuner() != NULL) { + if (mModuleClients[i]->audio()) { + if (oldestAudio == 0) { + oldestAudio = mModuleClients[i]; + } + allocatedAudio++; + } else { + if (oldestTuner == 0) { + oldestTuner = mModuleClients[i]; + } + allocatedTuners++; + } + } + } + + const struct radio_tuner *halTuner; + sp<ModuleClient> preemtedClient; + if (audio) { + if (allocatedAudio >= mProperties.num_audio_sources) { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + preemtedClient = oldestAudio; + } + } else { + if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) { + if (allocatedTuners != 0) { + ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch"); + preemtedClient = oldestTuner; + } else { + ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch"); + preemtedClient = oldestAudio; + } + } + } + if (preemtedClient != 0) { + halTuner = preemtedClient->getTuner(); + preemtedClient->setTuner(NULL); + mHwDevice->close_tuner(mHwDevice, halTuner); + if (preemtedClient->audio()) { + notifyDeviceConnection(false, ""); + } + } + + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio, + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + if (ret == 0) { + ALOGV("addClient() setTuner %p", halTuner); + moduleClient->setTuner(halTuner); + mModuleClients.add(moduleClient); + if (audio) { + notifyDeviceConnection(true, ""); + } + } else { + moduleClient.clear(); + } + + + ALOGV("addClient() DONE moduleClient %p", moduleClient.get()); + + return moduleClient; +} + +void RadioService::Module::removeClient(const sp<ModuleClient>& moduleClient) { + ALOGV("removeClient()"); + AutoMutex lock(mLock); + int ret; + ssize_t index = -1; + + for (size_t i = 0; i < mModuleClients.size(); i++) { + if (mModuleClients[i] == moduleClient) { + index = i; + break; + } + } + if (index == -1) { + return; + } + + mModuleClients.removeAt(index); + const struct radio_tuner *halTuner = moduleClient->getTuner(); + if (halTuner == NULL) { + return; + } + + mHwDevice->close_tuner(mHwDevice, halTuner); + if (moduleClient->audio()) { + notifyDeviceConnection(false, ""); + } + + mMute = true; + + if (mModuleClients.isEmpty()) { + return; + } + + // Tuner reallocation logic: + // When a client is removed and was controlling a tuner, this tuner will be allocated to a + // previously preempted client. This client will be notified by a callback with + // RADIO_EVENT_CONTROL indicating gain of control. + // - If a preempted client is waiting for an audio source and one becomes available: + // Allocate the tuner to the most recently added client waiting for an audio source + // - If not: + // Allocate the tuner to the most recently added client. + // Each time a tuner using the audio source is opened or closed, the audio policy manager is + // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER. + + sp<ModuleClient> youngestClient; + sp<ModuleClient> youngestClientAudio; + size_t allocatedTuners = 0; + size_t allocatedAudio = 0; + for (ssize_t i = mModuleClients.size() - 1; i >= 0; i--) { + if (mModuleClients[i]->getTuner() == NULL) { + if (mModuleClients[i]->audio()) { + if (youngestClientAudio == 0) { + youngestClientAudio = mModuleClients[i]; + } + } else { + if (youngestClient == 0) { + youngestClient = mModuleClients[i]; + } + } + } else { + if (mModuleClients[i]->audio()) { + allocatedAudio++; + } else { + allocatedTuners++; + } + } + } + + ALOG_ASSERT(allocatedTuners + allocatedAudio < mProperties.num_tuners, + "removeClient() removed client but no tuner available"); + + ALOG_ASSERT(!moduleClient->audio() || allocatedAudio < mProperties.num_audio_sources, + "removeClient() removed audio client but no tuner with audio available"); + + if (allocatedAudio < mProperties.num_audio_sources && youngestClientAudio != 0) { + youngestClient = youngestClientAudio; + } + + ALOG_ASSERT(youngestClient != 0, "removeClient() removed client no candidate found for tuner"); + + struct radio_hal_band_config halConfig = youngestClient->halConfig(); + ret = mHwDevice->open_tuner(mHwDevice, &halConfig, youngestClient->audio(), + RadioService::callback, moduleClient->callbackThread().get(), + &halTuner); + + if (ret == 0) { + youngestClient->setTuner(halTuner); + if (youngestClient->audio()) { + notifyDeviceConnection(true, ""); + } + } +} + +status_t RadioService::Module::setMute(bool mute) +{ + Mutex::Autolock _l(mLock); + if (mute != mMute) { + mMute = mute; + //TODO notifify audio policy manager of media activity on radio audio device + } + return NO_ERROR; +} + +status_t RadioService::Module::getMute(bool *mute) +{ + Mutex::Autolock _l(mLock); + *mute = mMute; + return NO_ERROR; +} + + +const struct radio_band_config *RadioService::Module::getDefaultConfig() const +{ + if (mProperties.num_bands == 0) { + return NULL; + } + return &mProperties.bands[0]; +} + +void RadioService::Module::notifyDeviceConnection(bool connected, + const char *address) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER, + connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + address, kRadioTunerAudioDeviceName); + IPCThreadState::self()->restoreCallingIdentity(token); +} + +#undef LOG_TAG +#define LOG_TAG "RadioService::ModuleClient" + +RadioService::ModuleClient::ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio) + : mModule(module), mClient(client), mConfig(*config), mAudio(audio), mTuner(NULL) +{ +} + +void RadioService::ModuleClient::onFirstRef() +{ + mCallbackThread = new CallbackThread(this); + IInterface::asBinder(mClient)->linkToDeath(this); +} + +RadioService::ModuleClient::~ModuleClient() { + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } +} + +status_t RadioService::ModuleClient::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +void RadioService::ModuleClient::detach() { + ALOGV("%s", __FUNCTION__); + sp<ModuleClient> strongMe = this; + { + AutoMutex lock(mLock); + if (mClient != 0) { + IInterface::asBinder(mClient)->unlinkToDeath(this); + mClient.clear(); + } + } + sp<Module> module = mModule.promote(); + if (module == 0) { + return; + } + module->removeClient(this); +} + +radio_hal_band_config_t RadioService::ModuleClient::halConfig() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mConfig.band; +} + +const struct radio_tuner *RadioService::ModuleClient::getTuner() const +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + return mTuner; +} + +void RadioService::ModuleClient::setTuner(const struct radio_tuner *tuner) +{ + ALOGV("%s %p", __FUNCTION__, this); + + AutoMutex lock(mLock); + mTuner = tuner; + ALOGV("%s locked", __FUNCTION__); + + radio_hal_event_t event; + event.type = RADIO_EVENT_CONTROL; + event.status = 0; + event.on = mTuner != NULL; + mCallbackThread->sendEvent(&event); + ALOGV("%s DONE", __FUNCTION__); + +} + +status_t RadioService::ModuleClient::setConfiguration(const struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + halConfig = config->band; + status = (status_t)mTuner->set_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig = *config; + } + } else { + mConfig = *config; + status == INVALID_OPERATION; + } + + return status; +} + +status_t RadioService::ModuleClient::getConfiguration(struct radio_band_config *config) +{ + AutoMutex lock(mLock); + status_t status = NO_ERROR; + ALOGV("%s locked", __FUNCTION__); + + if (mTuner != NULL) { + struct radio_hal_band_config halConfig; + status = (status_t)mTuner->get_configuration(mTuner, &halConfig); + if (status == NO_ERROR) { + mConfig.band = halConfig; + } + } + *config = mConfig; + + return status; +} + +status_t RadioService::ModuleClient::setMute(bool mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + if (mTuner == NULL || !mAudio) { + return INVALID_OPERATION; + } + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + module->setMute(mute); + return NO_ERROR; +} + +status_t RadioService::ModuleClient::getMute(bool *mute) +{ + sp<Module> module; + { + Mutex::Autolock _l(mLock); + ALOGV("%s locked", __FUNCTION__); + module = mModule.promote(); + if (module == 0) { + return NO_INIT; + } + } + return module->getMute(mute); +} + +status_t RadioService::ModuleClient::scan(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->scan(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::step(radio_direction_t direction, bool skipSubChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->step(mTuner, direction, skipSubChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::tune(unsigned int channel, unsigned int subChannel) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->tune(mTuner, channel, subChannel); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::cancel() +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->cancel(mTuner); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::getProgramInformation(struct radio_program_info *info) +{ + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + status_t status; + if (mTuner != NULL) { + status = (status_t)mTuner->get_program_information(mTuner, info); + } else { + status = INVALID_OPERATION; + } + return status; +} + +status_t RadioService::ModuleClient::hasControl(bool *hasControl) +{ + Mutex::Autolock lock(mLock); + ALOGV("%s locked", __FUNCTION__); + *hasControl = mTuner != NULL; + return NO_ERROR; +} + +void RadioService::ModuleClient::onCallbackEvent(const sp<IMemory>& eventMemory) +{ + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + + sp<IRadioClient> client; + { + AutoMutex lock(mLock); + ALOGV("%s locked", __FUNCTION__); + radio_event_t *event = (radio_event_t *)eventMemory->pointer(); + switch (event->type) { + case RADIO_EVENT_CONFIG: + mConfig.band = event->config.band; + event->config.region = mConfig.region; + break; + default: + break; + } + + client = mClient; + } + if (client != 0) { + client->onEvent(eventMemory); + } +} + + +void RadioService::ModuleClient::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for client %p", this); + detach(); +} + +}; // namespace android diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h new file mode 100644 index 0000000..49feda6 --- /dev/null +++ b/services/radio/RadioService.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ANDROID_HARDWARE_RADIO_SERVICE_H +#define ANDROID_HARDWARE_RADIO_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <radio/IRadioService.h> +#include <radio/IRadio.h> +#include <radio/IRadioClient.h> +#include <system/radio.h> +#include <hardware/radio.h> + +namespace android { + +class MemoryHeapBase; + +class RadioService : + public BinderService<RadioService>, + public BnRadioService +{ + friend class BinderService<RadioService>; + +public: + class ModuleClient; + class Module; + + static char const* getServiceName() { return "media.radio"; } + + RadioService(); + virtual ~RadioService(); + + // IRadioService + virtual status_t listModules(struct radio_properties *properties, + uint32_t *numModules); + + virtual status_t attach(radio_handle_t handle, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool withAudio, + sp<IRadio>& radio); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + class Module : public virtual RefBase { + public: + + Module(radio_hw_device* hwDevice, + struct radio_properties properties); + + virtual ~Module(); + + sp<ModuleClient> addClient(const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + void removeClient(const sp<ModuleClient>& moduleClient); + + status_t setMute(bool mute); + + status_t getMute(bool *mute); + + virtual status_t dump(int fd, const Vector<String16>& args); + + const struct radio_hw_device *hwDevice() const { return mHwDevice; } + const struct radio_properties properties() const { return mProperties; } + const struct radio_band_config *getDefaultConfig() const ; + + private: + + void notifyDeviceConnection(bool connected, const char *address); + + Mutex mLock; // protects mModuleClients + const struct radio_hw_device *mHwDevice; // HAL hardware device + const struct radio_properties mProperties; // cached hardware module properties + Vector< sp<ModuleClient> > mModuleClients; // list of attached clients + bool mMute; // radio audio source state + // when unmuted, audio is routed to the + // output device selected for media use case. + }; // class Module + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<ModuleClient>& moduleClient); + + virtual ~CallbackThread(); + + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + + void sendEvent(radio_hal_event_t *halEvent); + sp<IMemory> prepareEvent(radio_hal_event_t *halEvent); + + private: + wp<ModuleClient> mModuleClient; // client module the thread belongs to + Condition mCallbackCond; // condition signaled when a new event is posted + Mutex mCallbackLock; // protects mEventQueue + Vector< sp<IMemory> > mEventQueue; // pending callback events + sp<MemoryDealer> mMemoryDealer; // shared memory for callback event + }; // class CallbackThread + + class ModuleClient : public BnRadio, + public IBinder::DeathRecipient { + public: + + ModuleClient(const sp<Module>& module, + const sp<IRadioClient>& client, + const struct radio_band_config *config, + bool audio); + + virtual ~ModuleClient(); + + // IRadio + virtual void detach(); + + virtual status_t setConfiguration(const struct radio_band_config *config); + + virtual status_t getConfiguration(struct radio_band_config *config); + + virtual status_t setMute(bool mute); + + virtual status_t getMute(bool *mute); + + virtual status_t scan(radio_direction_t direction, bool skipSubChannel); + + virtual status_t step(radio_direction_t direction, bool skipSubChannel); + + virtual status_t tune(unsigned int channel, unsigned int subChannel); + + virtual status_t cancel(); + + virtual status_t getProgramInformation(struct radio_program_info *info); + + virtual status_t hasControl(bool *hasControl); + + virtual status_t dump(int fd, const Vector<String16>& args); + + sp<IRadioClient> client() const { return mClient; } + wp<Module> module() const { return mModule; } + radio_hal_band_config_t halConfig() const; + sp<CallbackThread> callbackThread() const { return mCallbackThread; } + void setTuner(const struct radio_tuner *tuner); + const struct radio_tuner *getTuner() const; + bool audio() const { return mAudio; } + + void onCallbackEvent(const sp<IMemory>& event); + + virtual void onFirstRef(); + + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + + mutable Mutex mLock; // protects mClient, mConfig and mTuner + wp<Module> mModule; // The module this client is attached to + sp<IRadioClient> mClient; // event callback binder interface + radio_band_config_t mConfig; // current band configuration + sp<CallbackThread> mCallbackThread; // event callback thread + const bool mAudio; + const struct radio_tuner *mTuner; // HAL tuner interface. NULL indicates that + // this client does not have control on any + // tuner + }; // class ModuleClient + + + static void callback(radio_hal_event_t *halEvent, void *cookie); + +private: + + virtual void onFirstRef(); + + static void convertProperties(radio_properties_t *properties, + const radio_hal_properties_t *halProperties); + Mutex mServiceLock; // protects mModules + volatile int32_t mNextUniqueId; // for module ID allocation + DefaultKeyedVector< radio_handle_t, sp<Module> > mModules; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_RADIO_SERVICE_H |