diff options
author | Mathias Agopian <mathias@google.com> | 2010-07-22 15:27:48 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2010-07-22 15:28:19 -0700 |
commit | 6d6b749b24fccfbbab41567fcf1eba1f14f5ad21 (patch) | |
tree | c4068f3bca655fb0a5ed30b8fc80f71462c31ae5 /services | |
parent | e7486695ced2dd135f6e2223b582adde4155b917 (diff) | |
download | frameworks_base-6d6b749b24fccfbbab41567fcf1eba1f14f5ad21.zip frameworks_base-6d6b749b24fccfbbab41567fcf1eba1f14f5ad21.tar.gz frameworks_base-6d6b749b24fccfbbab41567fcf1eba1f14f5ad21.tar.bz2 |
move native services under services/
moved surfaceflinger, audioflinger, cameraservice
all native services should now reside in this location.
Change-Id: Icd7336f7289db35df9c8c1857a5122bb8a6f1c86
Diffstat (limited to 'services')
68 files changed, 26022 insertions, 0 deletions
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp new file mode 100644 index 0000000..995e31c --- /dev/null +++ b/services/audioflinger/A2dpAudioInterface.cpp @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2008 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 <math.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface() +//{ +// AudioHardwareInterface* hw = 0; +// +// hw = AudioHardwareInterface::create(); +// LOGD("new A2dpAudioInterface(hw: %p)", hw); +// hw = new A2dpAudioInterface(hw); +// return hw; +//} + +A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) : + mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ + closeOutputStream((AudioStreamOut *)mOutput); + delete mHardwareInterface; +} + +status_t A2dpAudioInterface::initCheck() +{ + if (mHardwareInterface == 0) return NO_INIT; + return mHardwareInterface->initCheck(); +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { + LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); + return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); + } + + status_t err = 0; + + // only one output stream allowed + if (mOutput) { + if (status) + *status = -1; + return NULL; + } + + // create new output stream + A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); + if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { + mOutput = out; + mOutput->setBluetoothEnabled(mBluetoothEnabled); + mOutput->setSuspended(mSuspended); + } else { + delete out; + } + + if (status) + *status = err; + return mOutput; +} + +void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) { + if (mOutput == 0 || mOutput != out) { + mHardwareInterface->closeOutputStream(out); + } + else { + delete mOutput; + mOutput = 0; + } +} + + +AudioStreamIn* A2dpAudioInterface::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); +} + +void A2dpAudioInterface::closeInputStream(AudioStreamIn* in) +{ + return mHardwareInterface->closeInputStream(in); +} + +status_t A2dpAudioInterface::setMode(int mode) +{ + return mHardwareInterface->setMode(mode); +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ + return mHardwareInterface->setMicMute(state); +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ + return mHardwareInterface->getMicMute(state); +} + +status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key; + status_t status = NO_ERROR; + + LOGV("setParameters() %s", keyValuePairs.string()); + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + mBluetoothEnabled = (value == "true"); + if (mOutput) { + mOutput->setBluetoothEnabled(mBluetoothEnabled); + } + param.remove(key); + } + key = String8("A2dpSuspended"); + if (param.get(key, value) == NO_ERROR) { + mSuspended = (value == "true"); + if (mOutput) { + mOutput->setSuspended(mSuspended); + } + param.remove(key); + } + + if (param.size()) { + status_t hwStatus = mHardwareInterface->setParameters(param.toString()); + if (status == NO_ERROR) { + status = hwStatus; + } + } + + return status; +} + +String8 A2dpAudioInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + AudioParameter a2dpParam = AudioParameter(); + String8 value; + String8 key; + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + value = mBluetoothEnabled ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); + } + key = "A2dpSuspended"; + if (param.get(key, value) == NO_ERROR) { + value = mSuspended ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); + } + + String8 keyValuePairs = a2dpParam.toString(); + + if (param.size()) { + if (keyValuePairs != "") { + keyValuePairs += ";"; + } + keyValuePairs += mHardwareInterface->getParameters(param.toString()); + } + + LOGV("getParameters() %s", keyValuePairs.string()); + return keyValuePairs; +} + +size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ + return mHardwareInterface->setVoiceVolume(v); +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ + return mHardwareInterface->setMasterVolume(v); +} + +status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) +{ + return mHardwareInterface->dumpState(fd, args); +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : + mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL), + // assume BT enabled to start, this is safe because its only the + // enabled->disabled transition we are worried about + mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false) +{ + // use any address by default + strcpy(mA2dpAddress, "00:00:00:00:00:00"); + init(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( + uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate); + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())){ + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mDevice = device; + return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ + LOGV("A2dpAudioStreamOut destructor"); + standby(); + close(); + LOGV("A2dpAudioStreamOut destructor returning from close()"); +} + +ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) +{ + Mutex::Autolock lock(mLock); + + size_t remaining = bytes; + status_t status = -1; + + if (!mBluetoothEnabled || mClosing || mSuspended) { + LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \ + mBluetoothEnabled %d, mClosing %d, mSuspended %d", + mBluetoothEnabled, mClosing, mSuspended); + goto Error; + } + + status = init(); + if (status < 0) + goto Error; + + while (remaining > 0) { + status = a2dp_write(mData, buffer, remaining); + if (status <= 0) { + LOGE("a2dp_write failed err: %d\n", status); + goto Error; + } + remaining -= status; + buffer = ((char *)buffer) + status; + } + + mStandby = false; + + return bytes; + +Error: + // Simulate audio output timing in case of error + usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000); + + return status; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::init() +{ + if (!mData) { + status_t status = a2dp_init(44100, 2, &mData); + if (status < 0) { + LOGE("a2dp_init failed err: %d\n", status); + mData = NULL; + return status; + } + a2dp_set_sink(mData, mA2dpAddress); + } + + return 0; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() +{ + int result = 0; + + if (mClosing) { + LOGV("Ignore standby, closing"); + return result; + } + + Mutex::Autolock lock(mLock); + + if (!mStandby) { + result = a2dp_stop(mData); + if (result == 0) + mStandby = true; + } + + return result; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key = String8("a2dp_sink_address"); + status_t status = NO_ERROR; + int device; + LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string()); + + if (param.get(key, value) == NO_ERROR) { + if (value.length() != strlen("00:00:00:00:00:00")) { + status = BAD_VALUE; + } else { + setAddress(value.string()); + } + param.remove(key); + } + key = String8("closing"); + if (param.get(key, value) == NO_ERROR) { + mClosing = (value == "true"); + param.remove(key); + } + key = AudioParameter::keyRouting; + if (param.getInt(key, device) == NO_ERROR) { + if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) { + mDevice = device; + status = NO_ERROR; + } else { + status = BAD_VALUE; + } + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8("a2dp_sink_address"); + + if (param.get(key, value) == NO_ERROR) { + value = mA2dpAddress; + param.add(key, value); + } + key = AudioParameter::keyRouting; + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string()); + return param.toString(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) +{ + Mutex::Autolock lock(mLock); + + if (strlen(address) != strlen("00:00:00:00:00:00")) + return -EINVAL; + + strcpy(mA2dpAddress, address); + if (mData) + a2dp_set_sink(mData, mA2dpAddress); + + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled) +{ + LOGD("setBluetoothEnabled %d", enabled); + + Mutex::Autolock lock(mLock); + + mBluetoothEnabled = enabled; + if (!enabled) { + return close_l(); + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff) +{ + LOGV("setSuspended %d", onOff); + mSuspended = onOff; + standby(); + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ + Mutex::Autolock lock(mLock); + LOGV("A2dpAudioStreamOut::close() calling close_l()"); + return close_l(); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l() +{ + if (mData) { + LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)"); + a2dp_cleanup(mData); + mData = NULL; + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args) +{ + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames) +{ + //TODO: enable when supported by driver + return INVALID_OPERATION; +} + +}; // namespace android diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h new file mode 100644 index 0000000..48154f9 --- /dev/null +++ b/services/audioflinger/A2dpAudioInterface.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 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 A2DP_AUDIO_HARDWARE_H +#define A2DP_AUDIO_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioHardwareBase.h> + + +namespace android { + +class A2dpAudioInterface : public AudioHardwareBase +{ + class A2dpAudioStreamOut; + +public: + A2dpAudioInterface(AudioHardwareInterface* hw); + virtual ~A2dpAudioInterface(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + virtual status_t setMode(int mode); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); +// static AudioHardwareInterface* createA2dpInterface(); + +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + class A2dpAudioStreamOut : public AudioStreamOut { + public: + A2dpAudioStreamOut(); + virtual ~A2dpAudioStreamOut(); + status_t set(uint32_t device, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + virtual uint32_t sampleRate() const { return 44100; } + // SBC codec wants a multiple of 512 + virtual size_t bufferSize() const { return 512 * 20; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); + + private: + friend class A2dpAudioInterface; + status_t init(); + status_t close(); + status_t close_l(); + status_t setAddress(const char* address); + status_t setBluetoothEnabled(bool enabled); + status_t setSuspended(bool onOff); + + private: + int mFd; + bool mStandby; + int mStartCount; + int mRetryCount; + char mA2dpAddress[20]; + void* mData; + Mutex mLock; + bool mBluetoothEnabled; + uint32_t mDevice; + bool mClosing; + bool mSuspended; + }; + + friend class A2dpAudioStreamOut; + + A2dpAudioStreamOut* mOutput; + AudioHardwareInterface *mHardwareInterface; + char mA2dpAddress[20]; + bool mBluetoothEnabled; + bool mSuspended; +}; + + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // A2DP_AUDIO_HARDWARE_H diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk new file mode 100644 index 0000000..870c0b8 --- /dev/null +++ b/services/audioflinger/Android.mk @@ -0,0 +1,130 @@ +LOCAL_PATH:= $(call my-dir) + +#AUDIO_POLICY_TEST := true +#ENABLE_AUDIO_DUMP := true + +include $(CLEAR_VARS) + + +ifeq ($(AUDIO_POLICY_TEST),true) + ENABLE_AUDIO_DUMP := true +endif + + +LOCAL_SRC_FILES:= \ + AudioHardwareGeneric.cpp \ + AudioHardwareStub.cpp \ + AudioHardwareInterface.cpp + +ifeq ($(ENABLE_AUDIO_DUMP),true) + LOCAL_SRC_FILES += AudioDumpInterface.cpp + LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_CFLAGS += -DGENERIC_AUDIO +endif + +LOCAL_MODULE:= libaudiointerface + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SRC_FILES += A2dpAudioInterface.cpp + LOCAL_SHARED_LIBRARIES += liba2dp + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP + LOCAL_C_INCLUDES += $(call include-path-for, bluez) +endif + +include $(BUILD_STATIC_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyManagerBase.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_MODULE:= libaudiopolicybase + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_CFLAGS += -DWITH_A2DP +endif + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioFlinger.cpp \ + AudioMixer.cpp.arm \ + AudioResampler.cpp.arm \ + AudioResamplerSinc.cpp.arm \ + AudioResamplerCubic.cpp.arm \ + AudioPolicyService.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libbinder \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase + LOCAL_CFLAGS += -DGENERIC_AUDIO +else + LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy +endif + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_MODULE:= libaudioflinger + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP + LOCAL_SHARED_LIBRARIES += liba2dp +endif + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif +endif + +ifeq ($(BOARD_USE_LVMX),true) + LOCAL_CFLAGS += -DLVMX + LOCAL_C_INCLUDES += vendor/nxp + LOCAL_STATIC_LIBRARIES += liblifevibes + LOCAL_SHARED_LIBRARIES += liblvmxservice +# LOCAL_SHARED_LIBRARIES += liblvmxipc +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h new file mode 100644 index 0000000..81c5c39 --- /dev/null +++ b/services/audioflinger/AudioBufferProvider.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 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_BUFFER_PROVIDER_H +#define ANDROID_AUDIO_BUFFER_PROVIDER_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Errors.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioBufferProvider +{ +public: + + struct Buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frameCount; + }; + + virtual ~AudioBufferProvider() {} + + virtual status_t getNextBuffer(Buffer* buffer) = 0; + virtual void releaseBuffer(Buffer* buffer) = 0; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H diff --git a/services/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp new file mode 100644 index 0000000..a018b4c --- /dev/null +++ b/services/audioflinger/AudioDumpInterface.cpp @@ -0,0 +1,531 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.cpp +** +** Copyright 2008, 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 "AudioFlingerDump" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "AudioDumpInterface.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) + : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) +{ + if(hw == 0) { + LOGE("Dump construct hw = 0"); + } + mFinalInterface = hw; + LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface); +} + + +AudioDumpInterface::~AudioDumpInterface() +{ + for (size_t i = 0; i < mOutputs.size(); i++) { + closeOutputStream((AudioStreamOut *)mOutputs[i]); + } + if(mFinalInterface) delete mFinalInterface; +} + + +AudioStreamOut* AudioDumpInterface::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AudioStreamOut* outFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO; + uint32_t lRate = 44100; + + + if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) { + outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); + if (outFinal != 0) { + lFormat = outFinal->format(); + lChannels = outFinal->channels(); + lRate = outFinal->sampleRate(); + if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { + mFirstHwOutput = false; + } + } + } else { + if (format != 0 && *format != 0) { + lFormat = *format; + } else { + lFormat = AudioSystem::PCM_16_BIT; + } + if (channels != 0 && *channels != 0) { + lChannels = *channels; + } else { + lChannels = AudioSystem::CHANNEL_OUT_STEREO; + } + if (sampleRate != 0 && *sampleRate != 0) { + lRate = *sampleRate; + } else { + lRate = 44100; + } + if (status) *status = NO_ERROR; + } + LOGV("openOutputStream(), outFinal %p", outFinal); + + AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal, + devices, lFormat, lChannels, lRate); + mOutputs.add(dumOutput); + + return dumOutput; +} + +void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) +{ + AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out; + + if (mOutputs.indexOf(dumpOut) < 0) { + LOGW("Attempt to close invalid output stream"); + return; + } + + LOGV("closeOutputStream() output %p", out); + + dumpOut->standby(); + if (dumpOut->finalStream() != NULL) { + mFinalInterface->closeOutputStream(dumpOut->finalStream()); + mFirstHwOutput = true; + } + + mOutputs.remove(dumpOut); + delete dumpOut; +} + +AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + AudioStreamIn* inFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; + uint32_t lRate = 8000; + + + if (mInputs.size() == 0) { + inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); + if (inFinal == 0) return 0; + + lFormat = inFinal->format(); + lChannels = inFinal->channels(); + lRate = inFinal->sampleRate(); + } else { + if (format != 0 && *format != 0) lFormat = *format; + if (channels != 0 && *channels != 0) lChannels = *channels; + if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; + if (status) *status = NO_ERROR; + } + LOGV("openInputStream(), inFinal %p", inFinal); + + AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal, + devices, lFormat, lChannels, lRate); + mInputs.add(dumInput); + + return dumInput; +} +void AudioDumpInterface::closeInputStream(AudioStreamIn* in) +{ + AudioStreamInDump *dumpIn = (AudioStreamInDump *)in; + + if (mInputs.indexOf(dumpIn) < 0) { + LOGW("Attempt to close invalid input stream"); + return; + } + dumpIn->standby(); + if (dumpIn->finalStream() != NULL) { + mFinalInterface->closeInputStream(dumpIn->finalStream()); + } + + mInputs.remove(dumpIn); + delete dumpIn; +} + + +status_t AudioDumpInterface::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + int valueInt; + LOGV("setParameters %s", keyValuePairs.string()); + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + mFileName = value; + param.remove(String8("test_cmd_file_name")); + } + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); + param.remove(String8("test_cmd_policy")); + mPolicyCommands = param.toString(); + LOGV("test_cmd_policy command %s written", mPolicyCommands.string()); + return NO_ERROR; + } + + if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioDumpInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + AudioParameter response; + String8 value; + +// LOGV("getParameters %s", keys.string()); + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); + if (mPolicyCommands.length() != 0) { + response = AudioParameter(mPolicyCommands); + response.addInt(String8("test_cmd_policy"), 1); + } else { + response.addInt(String8("test_cmd_policy"), 0); + } + param.remove(String8("test_cmd_policy")); +// LOGV("test_cmd_policy command %s read", mPolicyCommands.string()); + } + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + response.add(String8("test_cmd_file_name"), mFileName); + param.remove(String8("test_cmd_file_name")); + } + + String8 keyValuePairs = response.toString(); + + if (param.size() && mFinalInterface != 0 ) { + keyValuePairs += ";"; + keyValuePairs += mFinalInterface->getParameters(param.toString()); + } + + return keyValuePairs; +} + + +// ---------------------------------------------------------------------------- + +AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate) + : mInterface(interface), mId(id), + mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices), + mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0) +{ + LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); +} + + +AudioStreamOutDump::~AudioStreamOutDump() +{ + LOGV("AudioStreamOutDump destructor"); + Close(); +} + +ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) +{ + ssize_t ret; + + if (mFinalStream) { + ret = mFinalStream->write(buffer, bytes); + } else { + usleep((bytes * 1000000) / frameSize() / sampleRate()); + ret = bytes; + } + if(!mOutFile) { + if (mInterface->fileName() != "") { + char name[255]; + sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mOutFile = fopen(name, "wb"); + LOGV("Opening dump file %s, fh %p", name, mOutFile); + } + } + if (mOutFile) { + fwrite(buffer, bytes, 1, mOutFile); + } + return ret; +} + +status_t AudioStreamOutDump::standby() +{ + LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); + + Close(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +uint32_t AudioStreamOutDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamOutDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamOutDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} +int AudioStreamOutDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} +uint32_t AudioStreamOutDump::latency() const +{ + if (mFinalStream != 0 ) return mFinalStream->latency(); + return 0; +} +status_t AudioStreamOutDump::setVolume(float left, float right) +{ + if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right); + return NO_ERROR; +} +status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) +{ + LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string()); + + if (mFinalStream != 0 ) { + return mFinalStream->setParameters(keyValuePairs); + } + + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + int valueInt; + status_t status = NO_ERROR; + + if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) { + mId = valueInt; + } + + if (param.getInt(String8("format"), valueInt) == NO_ERROR) { + if (mOutFile == 0) { + mFormat = valueInt; + } else { + status = INVALID_OPERATION; + } + } + if (param.getInt(String8("channels"), valueInt) == NO_ERROR) { + if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) { + mChannels = valueInt; + } else { + status = BAD_VALUE; + } + } + if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) { + if (valueInt > 0 && valueInt <= 48000) { + if (mOutFile == 0) { + mSampleRate = valueInt; + } else { + status = INVALID_OPERATION; + } + } else { + status = BAD_VALUE; + } + } + return status; +} + +String8 AudioStreamOutDump::getParameters(const String8& keys) +{ + if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); + + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} + +void AudioStreamOutDump::Close() +{ + if(mOutFile) { + fclose(mOutFile); + mOutFile = 0; + } +} + +status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames) +{ + if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames); + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate) + : mInterface(interface), mId(id), + mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), + mBufferSize(1024), mFinalStream(finalStream), mInFile(0) +{ + LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); +} + + +AudioStreamInDump::~AudioStreamInDump() +{ + Close(); +} + +ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) +{ + if (mFinalStream) { + return mFinalStream->read(buffer, bytes); + } + + usleep((bytes * 1000000) / frameSize() / sampleRate()); + + if(!mInFile) { + char name[255]; + strcpy(name, "/sdcard/music/sine440"); + if (channels() == AudioSystem::CHANNEL_IN_MONO) { + strcat(name, "_mo"); + } else { + strcat(name, "_st"); + } + if (format() == AudioSystem::PCM_16_BIT) { + strcat(name, "_16b"); + } else { + strcat(name, "_8b"); + } + if (sampleRate() < 16000) { + strcat(name, "_8k"); + } else if (sampleRate() < 32000) { + strcat(name, "_22k"); + } else if (sampleRate() < 48000) { + strcat(name, "_44k"); + } else { + strcat(name, "_48k"); + } + strcat(name, ".wav"); + mInFile = fopen(name, "rb"); + LOGV("Opening dump file %s, fh %p", name, mInFile); + if (mInFile) { + fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } + + } + if (mInFile) { + ssize_t bytesRead = fread(buffer, bytes, 1, mInFile); + if (bytesRead != bytes) { + fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile); + } + } + return bytes; +} + +status_t AudioStreamInDump::standby() +{ + LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream); + + Close(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +status_t AudioStreamInDump::setGain(float gain) +{ + if (mFinalStream != 0 ) return mFinalStream->setGain(gain); + return NO_ERROR; +} + +uint32_t AudioStreamInDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamInDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamInDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} + +int AudioStreamInDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} + +status_t AudioStreamInDump::setParameters(const String8& keyValuePairs) +{ + LOGV("AudioStreamInDump::setParameters()"); + if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioStreamInDump::getParameters(const String8& keys) +{ + if (mFinalStream != 0 ) return mFinalStream->getParameters(keys); + + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +unsigned int AudioStreamInDump::getInputFramesLost() const +{ + if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost(); + return 0; +} + +status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} + +void AudioStreamInDump::Close() +{ + if(mInFile) { + fclose(mInFile); + mInFile = 0; + } +} +}; // namespace android diff --git a/services/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h new file mode 100644 index 0000000..4c62b3e --- /dev/null +++ b/services/audioflinger/AudioDumpInterface.h @@ -0,0 +1,166 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.h +** +** Copyright 2008, 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_DUMP_INTERFACE_H +#define ANDROID_AUDIO_DUMP_INTERFACE_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/String8.h> +#include <utils/SortedVector.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +#define AUDIO_DUMP_WAVE_HDR_SIZE 44 + +class AudioDumpInterface; + +class AudioStreamOutDump : public AudioStreamOut { +public: + AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); + ~AudioStreamOutDump(); + + virtual ssize_t write(const void* buffer, size_t bytes); + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + virtual uint32_t latency() const; + virtual status_t setVolume(float left, float right); + virtual status_t standby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t dump(int fd, const Vector<String16>& args); + void Close(void); + AudioStreamOut* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } + int getId() { return mId; } + virtual status_t getRenderPosition(uint32_t *dspFrames); + +private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mLatency; // + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; + AudioStreamOut *mFinalStream; + FILE *mOutFile; // output file + int mFileCount; +}; + +class AudioStreamInDump : public AudioStreamIn { +public: + AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); + ~AudioStreamInDump(); + + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + + virtual status_t setGain(float gain); + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t standby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const; + virtual status_t dump(int fd, const Vector<String16>& args); + void Close(void); + AudioStreamIn* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } + +private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; + AudioStreamIn *mFinalStream; + FILE *mInFile; // output file +}; + +class AudioDumpInterface : public AudioHardwareBase +{ + +public: + AudioDumpInterface(AudioHardwareInterface* hw); + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual ~AudioDumpInterface(); + + virtual status_t initCheck() + {return mFinalInterface->initCheck();} + virtual status_t setVoiceVolume(float volume) + {return mFinalInterface->setVoiceVolume(volume);} + virtual status_t setMasterVolume(float volume) + {return mFinalInterface->setMasterVolume(volume);} + + // mic mute + virtual status_t setMicMute(bool state) + {return mFinalInterface->setMicMute(state);} + virtual status_t getMicMute(bool* state) + {return mFinalInterface->getMicMute(state);} + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + + virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } + + String8 fileName() const { return mFileName; } +protected: + + AudioHardwareInterface *mFinalInterface; + SortedVector<AudioStreamOutDump *> mOutputs; + bool mFirstHwOutput; + SortedVector<AudioStreamInDump *> mInputs; + Mutex mLock; + String8 mPolicyCommands; + String8 mFileName; +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_DUMP_INTERFACE_H diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp new file mode 100644 index 0000000..2414e8d --- /dev/null +++ b/services/audioflinger/AudioFlinger.cpp @@ -0,0 +1,4055 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.cpp +** +** 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 "AudioFlinger" +//#define LOG_NDEBUG 0 + +#include <math.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> + +#include <cutils/properties.h> + +#include <media/AudioTrack.h> +#include <media/AudioRecord.h> + +#include <private/media/AudioTrackShared.h> + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include "AudioMixer.h" +#include "AudioFlinger.h" + +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + +#ifdef LVMX +#include "lifevibes.h" +#endif + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n"; +static const char* kHardwareLockedString = "Hardware lock is taken\n"; + +//static const nsecs_t kStandbyTimeInNsecs = seconds(3); +static const float MAX_GAIN = 4096.0f; + +// retry counts for buffer fill timeout +// 50 * ~20msecs = 1 second +static const int8_t kMaxTrackRetries = 50; +static const int8_t kMaxTrackStartupRetries = 50; +// allow less retry attempts on direct output thread. +// direct outputs can be a scarce resource in audio hardware and should +// be released as quickly as possible. +static const int8_t kMaxTrackRetriesDirect = 2; + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 20000; + +static const nsecs_t kWarningThrottle = seconds(5); + + +#define AUDIOFLINGER_SECURITY_ENABLED 1 + +// ---------------------------------------------------------------------------- + +static bool recordingAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); + if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO"))) + LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest"); + return true; +#endif +} + +static bool settingsAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"))) + LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest"); + return true; +#endif +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioFlinger() + : BnAudioFlinger(), + mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0) +{ + mHardwareStatus = AUDIO_HW_IDLE; + + mAudioHardware = AudioHardwareInterface::create(); + + mHardwareStatus = AUDIO_HW_INIT; + if (mAudioHardware->initCheck() == NO_ERROR) { + // open 16-bit output stream for s/w mixer + + setMode(AudioSystem::MODE_NORMAL); + + setMasterVolume(1.0f); + setMasterMute(false); + } else { + LOGE("Couldn't even initialize the stubbed audio hardware!"); + } +#ifdef LVMX + LifeVibes::init(); +#endif +} + +AudioFlinger::~AudioFlinger() +{ + while (!mRecordThreads.isEmpty()) { + // closeInput() will remove first entry from mRecordThreads + closeInput(mRecordThreads.keyAt(0)); + } + while (!mPlaybackThreads.isEmpty()) { + // closeOutput() will remove first entry from mPlaybackThreads + closeOutput(mPlaybackThreads.keyAt(0)); + } + if (mAudioHardware) { + delete mAudioHardware; + } +} + + + +status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append("Clients:\n"); + for (size_t i = 0; i < mClients.size(); ++i) { + wp<Client> wClient = mClients.valueAt(i); + if (wClient != 0) { + sp<Client> client = wClient.promote(); + if (client != 0) { + snprintf(buffer, SIZE, " pid: %d\n", client->pid()); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + int hardwareStatus = mHardwareStatus; + + snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +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 AudioFlinger::dump(int fd, const Vector<String16>& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd, args); + } else { + // get state of hardware lock + bool hardwareLocked = tryLock(mHardwareLock); + if (!hardwareLocked) { + String8 result(kHardwareLockedString); + write(fd, result.string(), result.size()); + } else { + mHardwareLock.unlock(); + } + + bool locked = tryLock(mLock); + + // failed to lock - AudioFlinger is probably deadlocked + if (!locked) { + String8 result(kDeadlockedString); + write(fd, result.string(), result.size()); + } + + dumpClients(fd, args); + dumpInternals(fd, args); + + // dump playback threads + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads.valueAt(i)->dump(fd, args); + } + + // dump record threads + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads.valueAt(i)->dump(fd, args); + } + + if (mAudioHardware) { + mAudioHardware->dumpState(fd, args); + } + if (locked) mLock.unlock(); + } + return NO_ERROR; +} + + +// IAudioFlinger interface + + +sp<IAudioTrack> AudioFlinger::createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + int output, + status_t *status) +{ + sp<PlaybackThread::Track> track; + sp<TrackHandle> trackHandle; + sp<Client> client; + wp<Client> wclient; + status_t lStatus; + + if (streamType >= AudioSystem::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGE("unknown output thread"); + lStatus = BAD_VALUE; + goto Exit; + } + + wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + track = thread->createTrack_l(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } + if (lStatus == NO_ERROR) { + trackHandle = new TrackHandle(track); + } else { + // remove local strong reference to Client before deleting the Track so that the Client + // destructor is called by the TrackBase destructor with mLock held + client.clear(); + track.clear(); + } + +Exit: + if(status) { + *status = lStatus; + } + return trackHandle; +} + +uint32_t AudioFlinger::sampleRate(int output) const +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("sampleRate() unknown thread %d", output); + return 0; + } + return thread->sampleRate(); +} + +int AudioFlinger::channelCount(int output) const +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("channelCount() unknown thread %d", output); + return 0; + } + return thread->channelCount(); +} + +int AudioFlinger::format(int output) const +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("format() unknown thread %d", output); + return 0; + } + return thread->format(); +} + +size_t AudioFlinger::frameCount(int output) const +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("frameCount() unknown thread %d", output); + return 0; + } + return thread->frameCount(); +} + +uint32_t AudioFlinger::latency(int output) const +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("latency() unknown thread %d", output); + return 0; + } + return thread->latency(); +} + +status_t AudioFlinger::setMasterVolume(float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // when hw supports master volume, don't scale in sw mixer + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (mAudioHardware->setMasterVolume(value) == NO_ERROR) { + value = 1.0f; + } + mHardwareStatus = AUDIO_HW_IDLE; + + mMasterVolume = value; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads.valueAt(i)->setMasterVolume(value); + + return NO_ERROR; +} + +status_t AudioFlinger::setMode(int mode) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setMode(%d)", mode); + return BAD_VALUE; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MODE; + status_t ret = mAudioHardware->setMode(mode); +#ifdef LVMX + if (NO_ERROR == ret) { + LifeVibes::setMode(mode); + } +#endif + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +status_t AudioFlinger::setMicMute(bool state) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; + status_t ret = mAudioHardware->setMicMute(state); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +bool AudioFlinger::getMicMute() const +{ + bool state = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; + mAudioHardware->getMicMute(&state); + mHardwareStatus = AUDIO_HW_IDLE; + return state; +} + +status_t AudioFlinger::setMasterMute(bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + mMasterMute = muted; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads.valueAt(i)->setMasterMute(muted); + + return NO_ERROR; +} + +float AudioFlinger::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::setStreamVolume(int stream, float value, int output) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + AutoMutex lock(mLock); + PlaybackThread *thread = NULL; + if (output) { + thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return BAD_VALUE; + } + } + + mStreamTypes[stream].volume = value; + + if (thread == NULL) { + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value); + } + } else { + thread->setStreamVolume(stream, value); + } + + return NO_ERROR; +} + +status_t AudioFlinger::setStreamMute(int stream, bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || + uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) { + return BAD_VALUE; + } + + mStreamTypes[stream].mute = muted; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads.valueAt(i)->setStreamMute(stream, muted); + + return NO_ERROR; +} + +float AudioFlinger::streamVolume(int stream, int output) const +{ + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return 0.0f; + } + + AutoMutex lock(mLock); + float volume; + if (output) { + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return 0.0f; + } + volume = thread->streamVolume(stream); + } else { + volume = mStreamTypes[stream].volume; + } + + return volume; +} + +bool AudioFlinger::streamMute(int stream) const +{ + if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) { + return true; + } + + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::isStreamActive(int stream) const +{ + Mutex::Autolock _l(mLock); + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isStreamActive(stream)) { + return true; + } + } + return false; +} + +status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) +{ + status_t result; + + LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d", + ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid()); + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + +#ifdef LVMX + AudioParameter param = AudioParameter(keyValuePairs); + LifeVibes::setParameters(ioHandle,keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + int device; + if (NO_ERROR != param.getInt(key, device)) { + device = -1; + } + + key = String8(LifevibesTag); + String8 value; + int musicEnabled = -1; + if (NO_ERROR == param.get(key, value)) { + if (value == LifevibesEnable) { + musicEnabled = 1; + } else if (value == LifevibesDisable) { + musicEnabled = 0; + } + } +#endif + + // ioHandle == 0 means the parameters are global to the audio hardware interface + if (ioHandle == 0) { + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + result = mAudioHardware->setParameters(keyValuePairs); +#ifdef LVMX + if ((NO_ERROR == result) && (musicEnabled != -1)) { + LifeVibes::enableMusic((bool) musicEnabled); + } +#endif + mHardwareStatus = AUDIO_HW_IDLE; + return result; + } + + // hold a strong ref on thread in case closeOutput() or closeInput() is called + // and the thread is exited once the lock is released + sp<ThreadBase> thread; + { + Mutex::Autolock _l(mLock); + thread = checkPlaybackThread_l(ioHandle); + if (thread == NULL) { + thread = checkRecordThread_l(ioHandle); + } + } + if (thread != NULL) { + result = thread->setParameters(keyValuePairs); +#ifdef LVMX + if ((NO_ERROR == result) && (device != -1)) { + LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device); + } +#endif + return result; + } + return BAD_VALUE; +} + +String8 AudioFlinger::getParameters(int ioHandle, const String8& keys) +{ +// LOGV("getParameters() io %d, keys %s, tid %d, calling tid %d", +// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid()); + + if (ioHandle == 0) { + return mAudioHardware->getParameters(keys); + } + + Mutex::Autolock _l(mLock); + + PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle); + if (playbackThread != NULL) { + return playbackThread->getParameters(keys); + } + RecordThread *recordThread = checkRecordThread_l(ioHandle); + if (recordThread != NULL) { + return recordThread->getParameters(keys); + } + return String8(""); +} + +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} + +unsigned int AudioFlinger::getInputFramesLost(int ioHandle) +{ + if (ioHandle == 0) { + return 0; + } + + Mutex::Autolock _l(mLock); + + RecordThread *recordThread = checkRecordThread_l(ioHandle); + if (recordThread != NULL) { + return recordThread->getInputFramesLost(); + } + return 0; +} + +status_t AudioFlinger::setVoiceVolume(float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_VOICE_VOLUME; + status_t ret = mAudioHardware->setVoiceVolume(value); + mHardwareStatus = AUDIO_HW_IDLE; + + return ret; +} + +status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) +{ + status_t status; + + Mutex::Autolock _l(mLock); + + PlaybackThread *playbackThread = checkPlaybackThread_l(output); + if (playbackThread != NULL) { + return playbackThread->getRenderPosition(halFrames, dspFrames); + } + + return BAD_VALUE; +} + +void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) +{ + + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + sp<IBinder> binder = client->asBinder(); + if (mNotificationClients.indexOf(binder) < 0) { + LOGV("Adding notification client %p", binder.get()); + binder->linkToDeath(this); + mNotificationClients.add(binder); + } + + // the config change is always sent from playback or record threads to avoid deadlock + // with AudioSystem::gLock + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED); + } + + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED); + } +} + +void AudioFlinger::binderDied(const wp<IBinder>& who) { + + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + IBinder *binder = who.unsafe_get(); + + if (binder != NULL) { + int index = mNotificationClients.indexOf(binder); + if (index >= 0) { + LOGV("Removing notification client %p", binder); + mNotificationClients.removeAt(index); + } + } +} + +// audioConfigChanged_l() must be called with AudioFlinger::mLock held +void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) { + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i); + LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->ioConfigChanged(event, ioHandle, param2); + } +} + +// removeClient_l() must be called with AudioFlinger::mLock held +void AudioFlinger::removeClient_l(pid_t pid) +{ + LOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); + mClients.removeItem(pid); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id) + : Thread(false), + mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), + mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false) +{ +} + +AudioFlinger::ThreadBase::~ThreadBase() +{ + mParamCond.broadcast(); + mNewParameters.clear(); +} + +void AudioFlinger::ThreadBase::exit() +{ + // keep a strong ref on ourself so that we wont get + // destroyed in the middle of requestExitAndWait() + sp <ThreadBase> strongMe = this; + + LOGV("ThreadBase::exit"); + { + AutoMutex lock(&mLock); + mExiting = true; + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +uint32_t AudioFlinger::ThreadBase::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::ThreadBase::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::ThreadBase::format() const +{ + return mFormat; +} + +size_t AudioFlinger::ThreadBase::frameCount() const +{ + return mFrameCount; +} + +status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) +{ + status_t status; + + LOGV("ThreadBase::setParameters() %s", keyValuePairs.string()); + Mutex::Autolock _l(mLock); + + mNewParameters.add(keyValuePairs); + mWaitWorkCV.signal(); + // wait condition with timeout in case the thread loop has exited + // before the request could be processed + if (mParamCond.waitRelative(mLock, seconds(2)) == NO_ERROR) { + status = mParamStatus; + mWaitWorkCV.signal(); + } else { + status = TIMED_OUT; + } + return status; +} + +void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param) +{ + Mutex::Autolock _l(mLock); + sendConfigEvent_l(event, param); +} + +// sendConfigEvent_l() must be called with ThreadBase::mLock held +void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param) +{ + ConfigEvent *configEvent = new ConfigEvent(); + configEvent->mEvent = event; + configEvent->mParam = param; + mConfigEvents.add(configEvent); + LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param); + mWaitWorkCV.signal(); +} + +void AudioFlinger::ThreadBase::processConfigEvents() +{ + mLock.lock(); + while(!mConfigEvents.isEmpty()) { + LOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); + ConfigEvent *configEvent = mConfigEvents[0]; + mConfigEvents.removeAt(0); + // release mLock because audioConfigChanged() will lock AudioFlinger mLock + // before calling Audioflinger::audioConfigChanged_l() thus creating + // potential cross deadlock between AudioFlinger::mLock and mLock + mLock.unlock(); + audioConfigChanged(configEvent->mEvent, configEvent->mParam); + delete configEvent; + mLock.lock(); + } + mLock.unlock(); +} + +status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + bool locked = tryLock(mLock); + if (!locked) { + snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this); + write(fd, buffer, strlen(buffer)); + } + + snprintf(buffer, SIZE, "standby: %d\n", mStandby); + result.append(buffer); + snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, "Frame count: %d\n", mFrameCount); + result.append(buffer); + snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount); + result.append(buffer); + snprintf(buffer, SIZE, "Format: %d\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize); + result.append(buffer); + + snprintf(buffer, SIZE, "\nPending setParameters commands: \n"); + result.append(buffer); + result.append(" Index Command"); + for (size_t i = 0; i < mNewParameters.size(); ++i) { + snprintf(buffer, SIZE, "\n %02d ", i); + result.append(buffer); + result.append(mNewParameters[i]); + } + + snprintf(buffer, SIZE, "\n\nPending config events: \n"); + result.append(buffer); + snprintf(buffer, SIZE, " Index event param\n"); + result.append(buffer); + for (size_t i = 0; i < mConfigEvents.size(); i++) { + snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i]->mEvent, mConfigEvents[i]->mParam); + result.append(buffer); + } + result.append("\n"); + + write(fd, result.string(), result.size()); + + if (locked) { + mLock.unlock(); + } + return NO_ERROR; +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : ThreadBase(audioFlinger, id), + mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) +{ + readOutputParameters(); + + mMasterVolume = mAudioFlinger->masterVolume(); + mMasterMute = mAudioFlinger->masterMute(); + + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); + mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); + } + // notify client processes that a new input has been opened + sendConfigEvent(AudioSystem::OUTPUT_OPENED); +} + +AudioFlinger::PlaybackThread::~PlaybackThread() +{ + delete [] mMixBuffer; +} + +status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output thread %p tracks\n", this); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + + snprintf(buffer, SIZE, "Output thread %p active tracks\n", this); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mActiveTracks.size(); ++i) { + wp<Track> wTrack = mActiveTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this); + result.append(buffer); + snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + result.append(buffer); + snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); + result.append(buffer); + snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); + result.append(buffer); + snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); + result.append(buffer); + snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended); + result.append(buffer); + write(fd, result.string(), result.size()); + + dumpBase(fd, args); + + return NO_ERROR; +} + +// Thread virtuals +status_t AudioFlinger::PlaybackThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread %p ready to run", this); + return NO_ERROR; +} + +void AudioFlinger::PlaybackThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Playback Thread %p", this); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + +// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held +sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<Track> track; + status_t lStatus; + + if (mType == DIRECT) { + if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) { + LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p", + sampleRate, format, channelCount, mOutput); + lStatus = BAD_VALUE; + goto Exit; + } + } else { + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + } + + if (mOutput == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + { // scope for mLock + Mutex::Autolock _l(mLock); + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + if (track->getCblk() == NULL || track->name() < 0) { + lStatus = NO_MEMORY; + goto Exit; + } + mTracks.add(track); + } + lStatus = NO_ERROR; + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +uint32_t AudioFlinger::PlaybackThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) +{ +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setMasterVolume(audioOutputType, value); + } +#endif + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) +{ +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setMasterMute(audioOutputType, muted); + } +#endif + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::PlaybackThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::PlaybackThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) +{ +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setStreamVolume(audioOutputType, stream, value); + } +#endif + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) +{ +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setStreamMute(audioOutputType, stream, muted); + } +#endif + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::PlaybackThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::PlaybackThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::PlaybackThread::isStreamActive(int stream) const +{ + Mutex::Autolock _l(mLock); + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp<Track> t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->type() == stream) + return true; + } + return false; +} + +// addTrack_l() must be called with ThreadBase::mLock held +status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) +{ + status_t status = ALREADY_EXISTS; + + // set retry count for buffer fill + track->mRetryCount = kMaxTrackStartupRetries; + if (mActiveTracks.indexOf(track) < 0) { + // the track is newly added, make sure it fills up all its + // buffers before playing. This is to ensure the client will + // effectively get the latency it requested. + track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; + mActiveTracks.add(track); + status = NO_ERROR; + } + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; +} + +// destroyTrack_l() must be called with ThreadBase::mLock held +void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track) +{ + track->mState = TrackBase::TERMINATED; + if (mActiveTracks.indexOf(track) < 0) { + mTracks.remove(track); + deleteTrackName_l(track->name()); + } +} + +String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) +{ + return mOutput->getParameters(keys); +} + +void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { + AudioSystem::OutputDescriptor desc; + void *param2 = 0; + + LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param); + + switch (event) { + case AudioSystem::OUTPUT_OPENED: + case AudioSystem::OUTPUT_CONFIG_CHANGED: + desc.channels = mChannelCount; + desc.samplingRate = mSampleRate; + desc.format = mFormat; + desc.frameCount = mFrameCount; + desc.latency = latency(); + param2 = &desc; + break; + + case AudioSystem::STREAM_CONFIG_CHANGED: + param2 = ¶m; + case AudioSystem::OUTPUT_CLOSED: + default: + break; + } + Mutex::Autolock _l(mAudioFlinger->mLock); + mAudioFlinger->audioConfigChanged_l(event, mId, param2); +} + +void AudioFlinger::PlaybackThread::readOutputParameters() +{ + mSampleRate = mOutput->sampleRate(); + mChannelCount = AudioSystem::popCount(mOutput->channels()); + + mFormat = mOutput->format(); + mFrameSize = mOutput->frameSize(); + mFrameCount = mOutput->bufferSize() / mFrameSize; + + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + if (mMixBuffer != NULL) delete mMixBuffer; + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames) +{ + if (halFrames == 0 || dspFrames == 0) { + return BAD_VALUE; + } + if (mOutput == 0) { + return INVALID_OPERATION; + } + *halFrames = mBytesWritten/mOutput->frameSize(); + + return mOutput->getRenderPosition(dspFrames); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : PlaybackThread(audioFlinger, output, id), + mAudioMixer(0) +{ + mType = PlaybackThread::MIXER; + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + + // FIXME - Current mixer implementation only supports stereo output + if (mChannelCount == 1) { + LOGE("Invalid audio hardware channel count"); + } +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete mAudioMixer; +} + +bool AudioFlinger::MixerThread::threadLoop() +{ + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + uint32_t mixerStatus = MIXER_IDLE; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount * mFrameSize; + // FIXME: Relaxed timing because of a certain device that can't meet latency + // Should be reduced to 2x after the vendor fixes the driver issue + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3; + nsecs_t lastWarning = 0; + bool longStandbyExit = false; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; + + while (!exitPending()) + { + processConfigEvents(); + + mixerStatus = MIXER_IDLE; + { // scope for mLock + + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount * mFrameSize; + // FIXME: Relaxed timing because of a certain device that can't meet latency + // Should be reduced to 2x after the vendor fixes the driver issue + maxPeriod = seconds(mFrameCount) / mSampleRate * 3; + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); + } + + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; + + // put audio hardware into standby after short delay + if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || + mSuspended) { + if (!mStandby) { + LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended); + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + } + + if (!activeTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + + if (exitPending()) break; + + // wait until we have something to do... + LOGV("MixerThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("MixerThread %p TID %d waking up\n", this, gettid()); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = idleSleepTime; + continue; + } + } + + mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); + } + + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { + // mix buffers... + mAudioMixer->process(curBuf); + sleepTime = 0; + standbyTime = systemTime() + kStandbyTimeInNsecs; + } else { + // If no tracks are ready, sleep once for the duration of an output + // buffer size, then write 0s to the output + if (sleepTime == 0) { + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } + } else if (mBytesWritten != 0 || + (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) { + memset (curBuf, 0, mixBufferSize); + sleepTime = 0; + LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start"); + } + } + + if (mSuspended) { + sleepTime = idleSleepTime; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + mLastWriteTime = systemTime(); + mInWrite = true; + mBytesWritten += mixBufferSize; +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::process(audioOutputType, curBuf, mixBufferSize); + } +#endif + int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); + if (bytesWritten < 0) mBytesWritten -= mixBufferSize; + mNumWrites++; + mInWrite = false; + nsecs_t now = systemTime(); + nsecs_t delta = now - mLastWriteTime; + if (delta > maxPeriod) { + mNumDelayedWrites++; + if ((now - lastWarning) > kWarningThrottle) { + LOGW("write blocked for %llu msecs, %d delayed writes, thread %p", + ns2ms(delta), mNumDelayedWrites, this); + lastWarning = now; + } + if (mStandby) { + longStandbyExit = true; + } + } + mStandby = false; + } else { + usleep(sleepTime); + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + } + + if (!mStandby) { + mOutput->standby(); + } + + LOGV("MixerThread %p exiting", this); + return false; +} + +// prepareTracks_l() must be called with ThreadBase::mLock held +uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) +{ + + uint32_t mixerStatus = MIXER_IDLE; + // find out which tracks need to be processed + size_t count = activeTracks.size(); + + float masterVolume = mMasterVolume; + bool masterMute = mMasterMute; + +#ifdef LVMX + bool tracksConnectedChanged = false; + bool stateChanged = false; + + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) + { + int activeTypes = 0; + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + int iTracktype=track->type(); + activeTypes |= 1<<track->type(); + } + LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute); + } +#endif + + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused() && !track->isTerminated()) + { + //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || masterMute || track->isPausing() || + mStreamTypes[track->type()].mute) { + left = right = 0; + if (track->isPausing()) { + track->setPaused(); + } + } else { + // read original volumes with volume control + float typeVolume = mStreamTypes[track->type()].volume; +#ifdef LVMX + bool streamMute=false; + // read the volume from the LivesVibes audio engine. + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) + { + LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute); + if (streamMute) { + typeVolume = 0; + } + } +#endif + float v = masterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param = AudioMixer::VOLUME; + if (track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } + } else if (cblk->server != 0) { + // If the track is stopped before the first frame was mixed, + // do not apply ramp + param = AudioMixer::RAMP_VOLUME; + } +#ifdef LVMX + if ( tracksConnectedChanged || stateChanged ) + { + // only do the ramp when the volume is changed by the user / application + param = AudioMixer::VOLUME; + } +#endif + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + mixerStatus = MIXER_TRACKS_READY; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + tracksToRemove->add(track); + mAudioMixer->disable(AudioMixer::MIXING); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this); + tracksToRemove->add(track); + } else if (mixerStatus != MIXER_TRACKS_READY) { + mixerStatus = MIXER_TRACKS_ENABLED; + } + + mAudioMixer->disable(AudioMixer::MIXING); + } + } + } + + // remove all the tracks that need to be... + count = tracksToRemove->size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i<count ; i++) { + const sp<Track>& track = tracksToRemove->itemAt(i); + mActiveTracks.remove(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName_l(track->mName); + } + } + } + + return mixerStatus; +} + +void AudioFlinger::MixerThread::getTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks, + int streamType) +{ + LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size()); + Mutex::Autolock _l(mLock); + size_t size = mTracks.size(); + for (size_t i = 0; i < size; i++) { + sp<Track> t = mTracks[i]; + if (t->type() == streamType) { + tracks.add(t); + int j = mActiveTracks.indexOf(t); + if (j >= 0) { + t = mActiveTracks[j].promote(); + if (t != NULL) { + activeTracks.add(t); + } + } + } + } + + size = activeTracks.size(); + for (size_t i = 0; i < size; i++) { + mActiveTracks.remove(activeTracks[i]); + } + + size = tracks.size(); + for (size_t i = 0; i < size; i++) { + sp<Track> t = tracks[i]; + mTracks.remove(t); + deleteTrackName_l(t->name()); + } +} + +void AudioFlinger::MixerThread::putTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size()); + Mutex::Autolock _l(mLock); + size_t size = tracks.size(); + for (size_t i = 0; i < size ; i++) { + sp<Track> t = tracks[i]; + int name = getTrackName_l(); + + if (name < 0) return; + + t->mName = name; + t->mThread = this; + mTracks.add(t); + + int j = activeTracks.indexOf(t); + if (j >= 0) { + mActiveTracks.add(t); + // force buffer refilling and no ramp volume when the track is mixed for the first time + t->mFillingUpStatus = Track::FS_FILLING; + } + } +} + +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::MixerThread::getTrackName_l() +{ + return mAudioMixer->getTrackName(); +} + +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::MixerThread::deleteTrackName_l(int name) +{ + LOGV("remove track (%d) and delete from mixer", name); + mAudioMixer->deleteTrackName(name); +} + +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::MixerThread::checkForNewParameters_l() +{ + bool reconfig = false; + + while (!mNewParameters.isEmpty()) { + status_t status = NO_ERROR; + String8 keyValuePair = mNewParameters[0]; + AudioParameter param = AudioParameter(keyValuePair); + int value; + + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if (value != AudioSystem::PCM_16_BIT) { + status = BAD_VALUE; + } else { + reconfig = true; + } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + if (value != AudioSystem::CHANNEL_OUT_STEREO) { + status = BAD_VALUE; + } else { + reconfig = true; + } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mOutput->setParameters(keyValuePair); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + status = mOutput->setParameters(keyValuePair); + } + if (status == NO_ERROR && reconfig) { + delete mAudioMixer; + readOutputParameters(); + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + for (size_t i = 0; i < mTracks.size() ; i++) { + int name = getTrackName_l(); + if (name < 0) break; + mTracks[i]->mName = name; + // limit track sample rate to 2 x new output sample rate + if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) { + mTracks[i]->mCblk->sampleRate = 2 * sampleRate(); + } + } + sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + } + } + + mNewParameters.removeAt(0); + + mParamStatus = status; + mParamCond.signal(); + mWaitWorkCV.wait(mLock); + } + return reconfig; +} + +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + PlaybackThread::dumpInternals(fd, args); + + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +uint32_t AudioFlinger::MixerThread::activeSleepTimeUs() +{ + return (uint32_t)(mOutput->latency() * 1000) / 2; +} + +uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() +{ + return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000; +} + +// ---------------------------------------------------------------------------- +AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) + : PlaybackThread(audioFlinger, output, id), + mLeftVolume (1.0), mRightVolume(1.0) +{ + mType = PlaybackThread::DIRECT; +} + +AudioFlinger::DirectOutputThread::~DirectOutputThread() +{ +} + + +bool AudioFlinger::DirectOutputThread::threadLoop() +{ + uint32_t mixerStatus = MIXER_IDLE; + sp<Track> trackToRemove; + sp<Track> activeTrack; + nsecs_t standbyTime = systemTime(); + int8_t *curBuf; + size_t mixBufferSize = mFrameCount*mFrameSize; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; + // use shorter standby delay as on normal output to release + // hardware resources as soon as possible + nsecs_t standbyDelay = microseconds(activeSleepTime*2); + + + while (!exitPending()) + { + processConfigEvents(); + + mixerStatus = MIXER_IDLE; + + { // scope for the mLock + + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); + standbyDelay = microseconds(activeSleepTime*2); + } + + // put audio hardware into standby after short delay + if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) || + mSuspended) { + // wait until we have something to do... + if (!mStandby) { + LOGV("Audio hardware entering standby, mixer %p\n", this); + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + } + + if (!mActiveTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + + if (exitPending()) break; + + LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid()); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + standbyDelay; + sleepTime = idleSleepTime; + continue; + } + } + + // find out which tracks need to be processed + if (mActiveTracks.size() != 0) { + sp<Track> t = mActiveTracks[0].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused() && !track->isTerminated()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + float left, right; + if (track->isMuted() || mMasterMute || track->isPausing() || + mStreamTypes[track->type()].mute) { + left = right = 0; + if (track->isPausing()) { + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = v_clamped/MAX_GAIN; + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = v_clamped/MAX_GAIN; + } + + if (left != mLeftVolume || right != mRightVolume) { + mOutput->setVolume(left, right); + left = mLeftVolume; + right = mRightVolume; + } + + if (track->mFillingUpStatus == Track::FS_FILLED) { + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + } + } + + // reset retry count + track->mRetryCount = kMaxTrackRetriesDirect; + activeTrack = t; + mixerStatus = MIXER_TRACKS_READY; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + trackToRemove = track; + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + trackToRemove = track; + } else { + mixerStatus = MIXER_TRACKS_ENABLED; + } + } + } + } + + // remove all the tracks that need to be... + if (UNLIKELY(trackToRemove != 0)) { + mActiveTracks.remove(trackToRemove); + if (trackToRemove->isTerminated()) { + mTracks.remove(trackToRemove); + deleteTrackName_l(trackToRemove->mName); + } + } + } + + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { + AudioBufferProvider::Buffer buffer; + size_t frameCount = mFrameCount; + curBuf = (int8_t *)mMixBuffer; + // output audio to hardware + while(frameCount) { + buffer.frameCount = frameCount; + activeTrack->getNextBuffer(&buffer); + if (UNLIKELY(buffer.raw == 0)) { + memset(curBuf, 0, frameCount * mFrameSize); + break; + } + memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); + frameCount -= buffer.frameCount; + curBuf += buffer.frameCount * mFrameSize; + activeTrack->releaseBuffer(&buffer); + } + sleepTime = 0; + standbyTime = systemTime() + standbyDelay; + } else { + if (sleepTime == 0) { + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } + } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) { + memset (mMixBuffer, 0, mFrameCount * mFrameSize); + sleepTime = 0; + } + } + + if (mSuspended) { + sleepTime = idleSleepTime; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + mLastWriteTime = systemTime(); + mInWrite = true; + mBytesWritten += mixBufferSize; + int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); + if (bytesWritten < 0) mBytesWritten -= mixBufferSize; + mNumWrites++; + mInWrite = false; + mStandby = false; + } else { + usleep(sleepTime); + } + + // finally let go of removed track, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + trackToRemove.clear(); + activeTrack.clear(); + } + + if (!mStandby) { + mOutput->standby(); + } + + LOGV("DirectOutputThread %p exiting", this); + return false; +} + +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::DirectOutputThread::getTrackName_l() +{ + return 0; +} + +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name) +{ +} + +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() +{ + bool reconfig = false; + + while (!mNewParameters.isEmpty()) { + status_t status = NO_ERROR; + String8 keyValuePair = mNewParameters[0]; + AudioParameter param = AudioParameter(keyValuePair); + int value; + + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mOutput->setParameters(keyValuePair); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + status = mOutput->setParameters(keyValuePair); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters(); + sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + } + } + + mNewParameters.removeAt(0); + + mParamStatus = status; + mParamCond.signal(); + mWaitWorkCV.wait(mLock); + } + return reconfig; +} + +uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() +{ + uint32_t time; + if (AudioSystem::isLinearPCM(mFormat)) { + time = (uint32_t)(mOutput->latency() * 1000) / 2; + } else { + time = 10000; + } + return time; +} + +uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() +{ + uint32_t time; + if (AudioSystem::isLinearPCM(mFormat)) { + time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000; + } else { + time = 10000; + } + return time; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id) + : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX) +{ + mType = PlaybackThread::DUPLICATING; + addOutputTrack(mainThread); +} + +AudioFlinger::DuplicatingThread::~DuplicatingThread() +{ + for (size_t i = 0; i < mOutputTracks.size(); i++) { + mOutputTracks[i]->destroy(); + } + mOutputTracks.clear(); +} + +bool AudioFlinger::DuplicatingThread::threadLoop() +{ + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + uint32_t mixerStatus = MIXER_IDLE; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mFrameSize; + SortedVector< sp<OutputTrack> > outputTracks; + uint32_t writeFrames = 0; + uint32_t activeSleepTime = activeSleepTimeUs(); + uint32_t idleSleepTime = idleSleepTimeUs(); + uint32_t sleepTime = idleSleepTime; + + while (!exitPending()) + { + processConfigEvents(); + + mixerStatus = MIXER_IDLE; + { // scope for the mLock + + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + updateWaitTime(); + activeSleepTime = activeSleepTimeUs(); + idleSleepTime = idleSleepTimeUs(); + } + + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; + + for (size_t i = 0; i < mOutputTracks.size(); i++) { + outputTracks.add(mOutputTracks[i]); + } + + // put audio hardware into standby after short delay + if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || + mSuspended) { + if (!mStandby) { + for (size_t i = 0; i < outputTracks.size(); i++) { + outputTracks[i]->stop(); + } + mStandby = true; + mBytesWritten = 0; + } + + if (!activeTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + outputTracks.clear(); + + if (exitPending()) break; + + LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid()); + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = idleSleepTime; + continue; + } + } + + mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); + } + + if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { + // mix buffers... + if (outputsReady(outputTracks)) { + mAudioMixer->process(curBuf); + } else { + memset(curBuf, 0, mixBufferSize); + } + sleepTime = 0; + writeFrames = mFrameCount; + } else { + if (sleepTime == 0) { + if (mixerStatus == MIXER_TRACKS_ENABLED) { + sleepTime = activeSleepTime; + } else { + sleepTime = idleSleepTime; + } + } else if (mBytesWritten != 0) { + // flush remaining overflow buffers in output tracks + for (size_t i = 0; i < outputTracks.size(); i++) { + if (outputTracks[i]->isActive()) { + sleepTime = 0; + writeFrames = 0; + break; + } + } + } + } + + if (mSuspended) { + sleepTime = idleSleepTime; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + standbyTime = systemTime() + kStandbyTimeInNsecs; + for (size_t i = 0; i < outputTracks.size(); i++) { + outputTracks[i]->write(curBuf, writeFrames); + } + mStandby = false; + mBytesWritten += mixBufferSize; + } else { + usleep(sleepTime); + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + outputTracks.clear(); + } + + return false; +} + +void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) +{ + int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); + OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, + this, + mSampleRate, + mFormat, + mChannelCount, + frameCount); + if (outputTrack->cblk() != NULL) { + thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f); + mOutputTracks.add(outputTrack); + LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); + updateWaitTime(); + } +} + +void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) +{ + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mOutputTracks.size(); i++) { + if (mOutputTracks[i]->thread() == (ThreadBase *)thread) { + mOutputTracks[i]->destroy(); + mOutputTracks.removeAt(i); + updateWaitTime(); + return; + } + } + LOGV("removeOutputTrack(): unkonwn thread: %p", thread); +} + +void AudioFlinger::DuplicatingThread::updateWaitTime() +{ + mWaitTimeMs = UINT_MAX; + for (size_t i = 0; i < mOutputTracks.size(); i++) { + sp<ThreadBase> strong = mOutputTracks[i]->thread().promote(); + if (strong != NULL) { + uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate(); + if (waitTimeMs < mWaitTimeMs) { + mWaitTimeMs = waitTimeMs; + } + } + } +} + + +bool AudioFlinger::DuplicatingThread::outputsReady(SortedVector< sp<OutputTrack> > &outputTracks) +{ + for (size_t i = 0; i < outputTracks.size(); i++) { + sp <ThreadBase> thread = outputTracks[i]->thread().promote(); + if (thread == 0) { + LOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get()); + return false; + } + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (playbackThread->standby() && !playbackThread->isSuspended()) { + LOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get()); + return false; + } + } + return true; +} + +uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() +{ + return (mWaitTimeMs * 1000) / 2; +} + +// ---------------------------------------------------------------------------- + +// TrackBase constructor must be called with AudioFlinger::mLock held +AudioFlinger::ThreadBase::TrackBase::TrackBase( + const wp<ThreadBase>& thread, + const sp<Client>& client, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer) + : RefBase(), + mThread(thread), + mClient(client), + mCblk(0), + mFrameCount(0), + mState(IDLE), + mClientTid(-1), + mFormat(format), + mFlags(flags & ~SYSTEM_FLAGS_MASK) +{ + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + + // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); + size_t size = sizeof(audio_track_cblk_t); + size_t bufferSize = frameCount*channelCount*sizeof(int16_t); + if (sharedBuffer == 0) { + size += bufferSize; + } + + if (client != NULL) { + mCblkMemory = client->heap()->allocate(size); + if (mCblkMemory != 0) { + mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = (uint8_t)channelCount; + if (sharedBuffer == 0) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + } else { + mBuffer = sharedBuffer->pointer(); + } + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; + } + } else { + mCblk = (audio_track_cblk_t *)(new uint8_t[size]); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = (uint8_t)channelCount; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } +} + +AudioFlinger::ThreadBase::TrackBase::~TrackBase() +{ + if (mCblk) { + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + if (mClient == NULL) { + delete mCblk; + } + } + mCblkMemory.clear(); // and free the shared memory + if (mClient != NULL) { + Mutex::Autolock _l(mClient->audioFlinger()->mLock); + mClient.clear(); + } +} + +void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + buffer->raw = 0; + mFrameCount = buffer->frameCount; + step(); + buffer->frameCount = 0; +} + +bool AudioFlinger::ThreadBase::TrackBase::step() { + bool result; + audio_track_cblk_t* cblk = this->cblk(); + + result = cblk->stepServer(mFrameCount); + if (!result) { + LOGV("stepServer failed acquiring cblk mutex"); + mFlags |= STEPSERVER_FAILED; + } + return result; +} + +void AudioFlinger::ThreadBase::TrackBase::reset() { + audio_track_cblk_t* cblk = this->cblk(); + + cblk->user = 0; + cblk->server = 0; + cblk->userBase = 0; + cblk->serverBase = 0; + mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK); + LOGV("TrackBase::reset"); +} + +sp<IMemory> AudioFlinger::ThreadBase::TrackBase::getCblk() const +{ + return mCblkMemory; +} + +int AudioFlinger::ThreadBase::TrackBase::sampleRate() const { + return (int)mCblk->sampleRate; +} + +int AudioFlinger::ThreadBase::TrackBase::channelCount() const { + return (int)mCblk->channels; +} + +void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { + audio_track_cblk_t* cblk = this->cblk(); + int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize; + int8_t *bufferEnd = bufferStart + frames * cblk->frameSize; + + // Check validity of returned pointer in case the track control block would have been corrupted. + if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd || + ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) { + LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ + server %d, serverBase %d, user %d, userBase %d, channels %d", + bufferStart, bufferEnd, mBuffer, mBufferEnd, + cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channels); + return 0; + } + + return bufferStart; +} + +// ---------------------------------------------------------------------------- + +// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held +AudioFlinger::PlaybackThread::Track::Track( + const wp<ThreadBase>& thread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer) + : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer), + mMute(false), mSharedBuffer(sharedBuffer), mName(-1) +{ + if (mCblk != NULL) { + sp<ThreadBase> baseThread = thread.promote(); + if (baseThread != 0) { + PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get(); + mName = playbackThread->getTrackName_l(); + } + LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + if (mName < 0) { + LOGE("no more track names available"); + } + mVolume[0] = 1.0f; + mVolume[1] = 1.0f; + mStreamType = streamType; + // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of + // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack + mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t); + } +} + +AudioFlinger::PlaybackThread::Track::~Track() +{ + LOGV("PlaybackThread::Track destructor"); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + mState = TERMINATED; + } +} + +void AudioFlinger::PlaybackThread::Track::destroy() +{ + // NOTE: destroyTrack_l() can remove a strong reference to this Track + // by removing it from mTracks vector, so there is a risk that this Tracks's + // desctructor is called. As the destructor needs to lock mLock, + // we must acquire a strong reference on this Track before locking mLock + // here so that the destructor is called only when exiting this function. + // On the other hand, as long as Track::destroy() is only called by + // TrackHandle destructor, the TrackHandle still holds a strong ref on + // this Track with its member mTrack. + sp<Track> keep(this); + { // scope for mLock + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + if (!isOutputTrack()) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + } + AudioSystem::releaseOutput(thread->id()); + } + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->destroyTrack_l(this); + } + } +} + +void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u %08x %08x\n", + mName - AudioMixer::TRACK0, + (mClient == NULL) ? getpid() : mClient->pid(), + mStreamType, + mFormat, + mCblk->channels, + mFrameCount, + mState, + mMute, + mFillingUpStatus, + mCblk->sampleRate, + mCblk->volume[0], + mCblk->volume[1], + mCblk->server, + mCblk->user); +} + +status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesReady; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesReady = cblk->framesReady(); + + if (LIKELY(framesReady)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd; + if (framesReq > framesReady) { + framesReq = framesReady; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + LOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get()); + return NOT_ENOUGH_DATA; +} + +bool AudioFlinger::PlaybackThread::Track::isReady() const { + if (mFillingUpStatus != FS_FILLING) return true; + + if (mCblk->framesReady() >= mCblk->frameCount || + mCblk->forceReady) { + mFillingUpStatus = FS_FILLED; + mCblk->forceReady = 0; + return true; + } + return false; +} + +status_t AudioFlinger::PlaybackThread::Track::start() +{ + status_t status = NO_ERROR; + LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + int state = mState; + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (mState == PAUSED) { + mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this); + } else { + mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d) on thread %p", mName, this); + } + + if (!isOutputTrack() && state != ACTIVE && state != RESUMING) { + thread->mLock.unlock(); + status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } + if (status == NO_ERROR) { + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->addTrack_l(this); + } else { + mState = state; + } + } else { + status = BAD_VALUE; + } + return status; +} + +void AudioFlinger::PlaybackThread::Track::stop() +{ + LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + int state = mState; + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (playbackThread->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread); + } + if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) { + thread->mLock.unlock(); + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } + } +} + +void AudioFlinger::PlaybackThread::Track::pause() +{ + LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get()); + if (!isOutputTrack()) { + thread->mLock.unlock(); + AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + thread->mLock.lock(); + } + } + } +} + +void AudioFlinger::PlaybackThread::Track::flush() +{ + LOGV("flush(%d)", mName); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + return; + } + // No point remaining in PAUSED state after a flush => go to + // STOPPED state + mState = STOPPED; + + mCblk->lock.lock(); + // NOTE: reset() will reset cblk->user and cblk->server with + // the risk that at the same time, the AudioMixer is trying to read + // data. In this case, getNextBuffer() would return a NULL pointer + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! + reset(); + mCblk->lock.unlock(); + } +} + +void AudioFlinger::PlaybackThread::Track::reset() +{ + // Do not reset twice to avoid discarding data written just after a flush and before + // the audioflinger thread detects the track is stopped. + if (!mResetDone) { + TrackBase::reset(); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mCblk->forceReady = 0; + mFillingUpStatus = FS_FILLING; + mResetDone = true; + } +} + +void AudioFlinger::PlaybackThread::Track::mute(bool muted) +{ + mMute = muted; +} + +void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right) +{ + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +// RecordTrack constructor must be called with AudioFlinger::mLock held +AudioFlinger::RecordThread::RecordTrack::RecordTrack( + const wp<ThreadBase>& thread, + const sp<Client>& client, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags) + : TrackBase(thread, client, sampleRate, format, + channelCount, frameCount, flags, 0), + mOverflow(false) +{ + if (mCblk != NULL) { + LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer); + if (format == AudioSystem::PCM_16_BIT) { + mCblk->frameSize = channelCount * sizeof(int16_t); + } else if (format == AudioSystem::PCM_8_BIT) { + mCblk->frameSize = channelCount * sizeof(int8_t); + } else { + mCblk->frameSize = sizeof(int8_t); + } + } +} + +AudioFlinger::RecordThread::RecordTrack::~RecordTrack() +{ + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + AudioSystem::releaseInput(thread->id()); + } +} + +status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesAvail; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesAvail = cblk->framesAvailable_l(); + + if (LIKELY(framesAvail)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::RecordThread::RecordTrack::start() +{ + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + return recordThread->start(this); + } else { + return BAD_VALUE; + } +} + +void AudioFlinger::RecordThread::RecordTrack::stop() +{ + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + recordThread->stop(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; + } +} + +void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n", + (mClient == NULL) ? getpid() : mClient->pid(), + mFormat, + mCblk->channels, + mFrameCount, + mState, + mCblk->sampleRate, + mCblk->server, + mCblk->user); +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( + const wp<ThreadBase>& thread, + DuplicatingThread *sourceThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL), + mActive(false), mSourceThread(sourceThread) +{ + + PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); + if (mCblk != NULL) { + mCblk->out = 1; + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mOutBuffer.frameCount = 0; + playbackThread->mTracks.add(this); + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + } else { + LOGW("Error creating output track on thread %p", playbackThread); + } +} + +AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack() +{ + clearBufferQueue(); +} + +status_t AudioFlinger::PlaybackThread::OutputTrack::start() +{ + status_t status = Track::start(); + if (status != NO_ERROR) { + return status; + } + + mActive = true; + mRetryCount = 127; + return status; +} + +void AudioFlinger::PlaybackThread::OutputTrack::stop() +{ + Track::stop(); + clearBufferQueue(); + mOutBuffer.frameCount = 0; + mActive = false; +} + +bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames) +{ + Buffer *pInBuffer; + Buffer inBuffer; + uint32_t channels = mCblk->channels; + bool outputBufferFull = false; + inBuffer.frameCount = frames; + inBuffer.i16 = 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 (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOverFlowBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() %p no more buffers in queue", this); + } + } + } + } + + while (waitTimeLeftMs) { + // First write pending buffers, then new data + if (mBufferQueue.size()) { + pInBuffer = mBufferQueue.itemAt(0); + } else { + pInBuffer = &inBuffer; + } + + if (pInBuffer->frameCount == 0) { + break; + } + + if (mOutBuffer.frameCount == 0) { + mOutBuffer.frameCount = pInBuffer->frameCount; + nsecs_t startTime = systemTime(); + if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + LOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get()); + outputBufferFull = true; + break; + } + uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime); + if (waitTimeLeftMs >= waitTimeMs) { + waitTimeLeftMs -= waitTimeMs; + } else { + waitTimeLeftMs = 0; + } + } + + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + mCblk->stepUser(outFrames); + pInBuffer->frameCount -= outFrames; + pInBuffer->i16 += outFrames * channels; + mOutBuffer.frameCount -= outFrames; + mOutBuffer.i16 += outFrames * channels; + + if (pInBuffer->frameCount == 0) { + if (mBufferQueue.size()) { + mBufferQueue.removeAt(0); + delete [] pInBuffer->mBuffer; + delete pInBuffer; + LOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); + } else { + break; + } + } + } + + // If we could not write all frames, allocate a buffer and queue it for next time. + if (inBuffer.frameCount) { + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0 && !thread->standby()) { + if (mBufferQueue.size() < kMaxOverFlowBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size()); + } else { + LOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this); + } + } + } + + // 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) { + if (mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else if (mActive) { + stop(); + } + } + + return outputBufferFull; +} + +status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs) +{ + int active; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = buffer->frameCount; + +// LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); + buffer->frameCount = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + + if (framesAvail == 0) { + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (framesAvail == 0) { + active = mActive; + if (UNLIKELY(!active)) { + LOGV("Not active and NO_MORE_BUFFERS"); + return AudioTrack::NO_MORE_BUFFERS; + } + result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + if (result != NO_ERROR) { + return AudioTrack::NO_MORE_BUFFERS; + } + // read the server count again + start_loop_here: + framesAvail = cblk->framesAvailable_l(); + } + } + +// if (framesAvail < framesReq) { +// return AudioTrack::NO_MORE_BUFFERS; +// } + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + buffer->frameCount = framesReq; + buffer->raw = (void *)cblk->buffer(u); + return NO_ERROR; +} + + +void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() +{ + size_t size = mBufferQueue.size(); + Buffer *pBuffer; + + for (size_t i = 0; i < size; i++) { + pBuffer = mBufferQueue.itemAt(i); + delete [] pBuffer->mBuffer; + delete pBuffer; + } + mBufferQueue.clear(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) + : RefBase(), + mAudioFlinger(audioFlinger), + mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), + mPid(pid) +{ + // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer +} + +// Client destructor must be called with AudioFlinger::mLock held +AudioFlinger::Client::~Client() +{ + mAudioFlinger->removeClient_l(mPid); +} + +const sp<MemoryDealer>& AudioFlinger::Client::heap() const +{ + return mMemoryDealer; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) + : BnAudioTrack(), + mTrack(track) +{ +} + +AudioFlinger::TrackHandle::~TrackHandle() { + // just stop the track on deletion, associated resources + // will be freed from the main thread once all pending buffers have + // been played. Unless it's not in the active track list, in which + // case we free everything now... + mTrack->destroy(); +} + +status_t AudioFlinger::TrackHandle::start() { + return mTrack->start(); +} + +void AudioFlinger::TrackHandle::stop() { + mTrack->stop(); +} + +void AudioFlinger::TrackHandle::flush() { + mTrack->flush(); +} + +void AudioFlinger::TrackHandle::mute(bool e) { + mTrack->mute(e); +} + +void AudioFlinger::TrackHandle::pause() { + mTrack->pause(); +} + +void AudioFlinger::TrackHandle::setVolume(float left, float right) { + mTrack->setVolume(left, right); +} + +sp<IMemory> AudioFlinger::TrackHandle::getCblk() const { + return mTrack->getCblk(); +} + +status_t AudioFlinger::TrackHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioTrack::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +sp<IAudioRecord> AudioFlinger::openRecord( + pid_t pid, + int input, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status) +{ + sp<RecordThread::RecordTrack> recordTrack; + sp<RecordHandle> recordHandle; + sp<Client> client; + wp<Client> wclient; + status_t lStatus; + RecordThread *thread; + size_t inFrameCount; + + // check calling permissions + if (!recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } + + // add client to list + { // scope for mLock + Mutex::Autolock _l(mLock); + thread = checkRecordThread_l(input); + if (thread == NULL) { + lStatus = BAD_VALUE; + goto Exit; + } + + wclient = mClients.valueFor(pid); + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + + // create new record track. The record track uses one track in mHardwareMixerThread by convention. + recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate, + format, channelCount, frameCount, flags); + } + if (recordTrack->getCblk() == NULL) { + // remove local strong reference to Client before deleting the RecordTrack so that the Client + // destructor is called by the TrackBase destructor with mLock held + client.clear(); + recordTrack.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + + // return to handle to client + recordHandle = new RecordHandle(recordTrack); + lStatus = NO_ERROR; + +Exit: + if (status) { + *status = lStatus; + } + return recordHandle; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack) + : BnAudioRecord(), + mRecordTrack(recordTrack) +{ +} + +AudioFlinger::RecordHandle::~RecordHandle() { + stop(); +} + +status_t AudioFlinger::RecordHandle::start() { + LOGV("RecordHandle::start()"); + return mRecordTrack->start(); +} + +void AudioFlinger::RecordHandle::stop() { + LOGV("RecordHandle::stop()"); + mRecordTrack->stop(); +} + +sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { + return mRecordTrack->getCblk(); +} + +status_t AudioFlinger::RecordHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioRecord::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels, int id) : + ThreadBase(audioFlinger, id), + mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0) +{ + mReqChannelCount = AudioSystem::popCount(channels); + mReqSampleRate = sampleRate; + readInputParameters(); + sendConfigEvent(AudioSystem::INPUT_OPENED); +} + + +AudioFlinger::RecordThread::~RecordThread() +{ + delete[] mRsmpInBuffer; + if (mResampler != 0) { + delete mResampler; + delete[] mRsmpOutBuffer; + } +} + +void AudioFlinger::RecordThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Record Thread %p", this); + + run(buffer, PRIORITY_URGENT_AUDIO); +} + +bool AudioFlinger::RecordThread::threadLoop() +{ + AudioBufferProvider::Buffer buffer; + sp<RecordTrack> activeTrack; + + // start recording + while (!exitPending()) { + + processConfigEvents(); + + { // scope for mLock + Mutex::Autolock _l(mLock); + checkForNewParameters_l(); + if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { + if (!mStandby) { + mInput->standby(); + mStandby = true; + } + + if (exitPending()) break; + + LOGV("RecordThread: loop stopping"); + // go to sleep + mWaitWorkCV.wait(mLock); + LOGV("RecordThread: loop starting"); + continue; + } + if (mActiveTrack != 0) { + if (mActiveTrack->mState == TrackBase::PAUSING) { + if (!mStandby) { + mInput->standby(); + mStandby = true; + } + mActiveTrack.clear(); + mStartStopCond.broadcast(); + } else if (mActiveTrack->mState == TrackBase::RESUMING) { + if (mReqChannelCount != mActiveTrack->channelCount()) { + mActiveTrack.clear(); + mStartStopCond.broadcast(); + } else if (mBytesRead != 0) { + // record start succeeds only if first read from audio input + // succeeds + if (mBytesRead > 0) { + mActiveTrack->mState = TrackBase::ACTIVE; + } else { + mActiveTrack.clear(); + } + mStartStopCond.broadcast(); + } + mStandby = false; + } + } + } + + if (mActiveTrack != 0) { + if (mActiveTrack->mState != TrackBase::ACTIVE && + mActiveTrack->mState != TrackBase::RESUMING) { + usleep(5000); + continue; + } + buffer.frameCount = mFrameCount; + if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { + size_t framesOut = buffer.frameCount; + if (mResampler == 0) { + // no resampling + while (framesOut) { + size_t framesIn = mFrameCount - mRsmpInIndex; + if (framesIn) { + int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize; + int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize; + if (framesIn > framesOut) + framesIn = framesOut; + mRsmpInIndex += framesIn; + framesOut -= framesIn; + if (mChannelCount == mReqChannelCount || + mFormat != AudioSystem::PCM_16_BIT) { + memcpy(dst, src, framesIn * mFrameSize); + } else { + int16_t *src16 = (int16_t *)src; + int16_t *dst16 = (int16_t *)dst; + if (mChannelCount == 1) { + while (framesIn--) { + *dst16++ = *src16; + *dst16++ = *src16++; + } + } else { + while (framesIn--) { + *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1); + src16 += 2; + } + } + } + } + if (framesOut && mFrameCount == mRsmpInIndex) { + if (framesOut == mFrameCount && + (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { + mBytesRead = mInput->read(buffer.raw, mInputBytes); + framesOut = 0; + } else { + mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + mRsmpInIndex = 0; + } + if (mBytesRead < 0) { + LOGE("Error reading audio input"); + if (mActiveTrack->mState == TrackBase::ACTIVE) { + // Force input into standby so that it tries to + // recover at next read attempt + mInput->standby(); + usleep(5000); + } + mRsmpInIndex = mFrameCount; + framesOut = 0; + buffer.frameCount = 0; + } + } + } + } else { + // resampling + + memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t)); + // alter output frame count as if we were expecting stereo samples + if (mChannelCount == 1 && mReqChannelCount == 1) { + framesOut >>= 1; + } + mResampler->resample(mRsmpOutBuffer, framesOut, this); + // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer() + // are 32 bit aligned which should be always true. + if (mChannelCount == 2 && mReqChannelCount == 1) { + AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); + // the resampler always outputs stereo samples: do post stereo to mono conversion + int16_t *src = (int16_t *)mRsmpOutBuffer; + int16_t *dst = buffer.i16; + while (framesOut--) { + *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1); + src += 2; + } + } else { + AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); + } + + } + mActiveTrack->releaseBuffer(&buffer); + mActiveTrack->overflow(); + } + // client isn't retrieving buffers fast enough + else { + if (!mActiveTrack->setOverflow()) + LOGW("RecordThread: buffer overflow"); + // Release the processor for a while before asking for a new buffer. + // This will give the application more chance to read from the buffer and + // clear the overflow. + usleep(5000); + } + } + } + + if (!mStandby) { + mInput->standby(); + } + mActiveTrack.clear(); + + mStartStopCond.broadcast(); + + LOGV("RecordThread %p exiting", this); + return false; +} + +status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack) +{ + LOGV("RecordThread::start"); + sp <ThreadBase> strongMe = this; + status_t status = NO_ERROR; + { + AutoMutex lock(&mLock); + if (mActiveTrack != 0) { + if (recordTrack != mActiveTrack.get()) { + status = -EBUSY; + } else if (mActiveTrack->mState == TrackBase::PAUSING) { + mActiveTrack->mState = TrackBase::ACTIVE; + } + return status; + } + + recordTrack->mState = TrackBase::IDLE; + mActiveTrack = recordTrack; + mLock.unlock(); + status_t status = AudioSystem::startInput(mId); + mLock.lock(); + if (status != NO_ERROR) { + mActiveTrack.clear(); + return status; + } + mActiveTrack->mState = TrackBase::RESUMING; + mRsmpInIndex = mFrameCount; + mBytesRead = 0; + // signal thread to start + LOGV("Signal record thread"); + mWaitWorkCV.signal(); + // do not wait for mStartStopCond if exiting + if (mExiting) { + mActiveTrack.clear(); + status = INVALID_OPERATION; + goto startError; + } + mStartStopCond.wait(mLock); + if (mActiveTrack == 0) { + LOGV("Record failed to start"); + status = BAD_VALUE; + goto startError; + } + LOGV("Record started OK"); + return status; + } +startError: + AudioSystem::stopInput(mId); + return status; +} + +void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { + LOGV("RecordThread::stop"); + sp <ThreadBase> strongMe = this; + { + AutoMutex lock(&mLock); + if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { + mActiveTrack->mState = TrackBase::PAUSING; + // do not wait for mStartStopCond if exiting + if (mExiting) { + return; + } + mStartStopCond.wait(mLock); + // if we have been restarted, recordTrack == mActiveTrack.get() here + if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) { + mLock.unlock(); + AudioSystem::stopInput(mId); + mLock.lock(); + LOGV("Record stopped OK"); + } + } + } +} + +status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + pid_t pid = 0; + + snprintf(buffer, SIZE, "\nInput thread %p internals\n", this); + result.append(buffer); + + if (mActiveTrack != 0) { + result.append("Active Track:\n"); + result.append(" Clien Fmt Chn Buf S SRate Serv User\n"); + mActiveTrack->dump(buffer, SIZE); + result.append(buffer); + + snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex); + result.append(buffer); + snprintf(buffer, SIZE, "In size: %d\n", mInputBytes); + result.append(buffer); + snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != 0)); + result.append(buffer); + snprintf(buffer, SIZE, "Out channel count: %d\n", mReqChannelCount); + result.append(buffer); + snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate); + result.append(buffer); + + + } else { + result.append("No record client\n"); + } + write(fd, result.string(), result.size()); + + dumpBase(fd, args); + + return NO_ERROR; +} + +status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + size_t framesReq = buffer->frameCount; + size_t framesReady = mFrameCount - mRsmpInIndex; + int channelCount; + + if (framesReady == 0) { + mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + if (mBytesRead < 0) { + LOGE("RecordThread::getNextBuffer() Error reading audio input"); + if (mActiveTrack->mState == TrackBase::ACTIVE) { + // Force input into standby so that it tries to + // recover at next read attempt + mInput->standby(); + usleep(5000); + } + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; + } + mRsmpInIndex = 0; + framesReady = mFrameCount; + } + + if (framesReq > framesReady) { + framesReq = framesReady; + } + + if (mChannelCount == 1 && mReqChannelCount == 2) { + channelCount = 1; + } else { + channelCount = 2; + } + buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount; + buffer->frameCount = framesReq; + return NO_ERROR; +} + +void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + mRsmpInIndex += buffer->frameCount; + buffer->frameCount = 0; +} + +bool AudioFlinger::RecordThread::checkForNewParameters_l() +{ + bool reconfig = false; + + while (!mNewParameters.isEmpty()) { + status_t status = NO_ERROR; + String8 keyValuePair = mNewParameters[0]; + AudioParameter param = AudioParameter(keyValuePair); + int value; + int reqFormat = mFormat; + int reqSamplingRate = mReqSampleRate; + int reqChannelCount = mReqChannelCount; + + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reqSamplingRate = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + reqFormat = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + reqChannelCount = AudioSystem::popCount(value); + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (mActiveTrack != 0) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mInput->setParameters(keyValuePair); + if (status == INVALID_OPERATION) { + mInput->standby(); + status = mInput->setParameters(keyValuePair); + } + if (reconfig) { + if (status == BAD_VALUE && + reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT && + ((int)mInput->sampleRate() <= 2 * reqSamplingRate) && + (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) { + status = NO_ERROR; + } + if (status == NO_ERROR) { + readInputParameters(); + sendConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); + } + } + } + + mNewParameters.removeAt(0); + + mParamStatus = status; + mParamCond.signal(); + mWaitWorkCV.wait(mLock); + } + return reconfig; +} + +String8 AudioFlinger::RecordThread::getParameters(const String8& keys) +{ + return mInput->getParameters(keys); +} + +void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { + AudioSystem::OutputDescriptor desc; + void *param2 = 0; + + switch (event) { + case AudioSystem::INPUT_OPENED: + case AudioSystem::INPUT_CONFIG_CHANGED: + desc.channels = mChannelCount; + desc.samplingRate = mSampleRate; + desc.format = mFormat; + desc.frameCount = mFrameCount; + desc.latency = 0; + param2 = &desc; + break; + + case AudioSystem::INPUT_CLOSED: + default: + break; + } + Mutex::Autolock _l(mAudioFlinger->mLock); + mAudioFlinger->audioConfigChanged_l(event, mId, param2); +} + +void AudioFlinger::RecordThread::readInputParameters() +{ + if (mRsmpInBuffer) delete mRsmpInBuffer; + if (mRsmpOutBuffer) delete mRsmpOutBuffer; + if (mResampler) delete mResampler; + mResampler = 0; + + mSampleRate = mInput->sampleRate(); + mChannelCount = AudioSystem::popCount(mInput->channels()); + mFormat = mInput->format(); + mFrameSize = mInput->frameSize(); + mInputBytes = mInput->bufferSize(); + mFrameCount = mInputBytes / mFrameSize; + mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount]; + + if (mSampleRate != mReqSampleRate && mChannelCount < 3 && mReqChannelCount < 3) + { + int channelCount; + // optmization: if mono to mono, use the resampler in stereo to stereo mode to avoid + // stereo to mono post process as the resampler always outputs stereo. + if (mChannelCount == 1 && mReqChannelCount == 2) { + channelCount = 1; + } else { + channelCount = 2; + } + mResampler = AudioResampler::create(16, channelCount, mReqSampleRate); + mResampler->setSampleRate(mSampleRate); + mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN); + mRsmpOutBuffer = new int32_t[mFrameCount * 2]; + + // optmization: if mono to mono, alter input frame count as if we were inputing stereo samples + if (mChannelCount == 1 && mReqChannelCount == 1) { + mFrameCount >>= 1; + } + + } + mRsmpInIndex = mFrameCount; +} + +unsigned int AudioFlinger::RecordThread::getInputFramesLost() +{ + return mInput->getInputFramesLost(); +} + +// ---------------------------------------------------------------------------- + +int AudioFlinger::openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + uint32_t flags) +{ + status_t status; + PlaybackThread *thread = NULL; + mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; + uint32_t format = pFormat ? *pFormat : 0; + uint32_t channels = pChannels ? *pChannels : 0; + uint32_t latency = pLatencyMs ? *pLatencyMs : 0; + + LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x", + pDevices ? *pDevices : 0, + samplingRate, + format, + channels, + flags); + + if (pDevices == NULL || *pDevices == 0) { + return 0; + } + Mutex::Autolock _l(mLock); + + AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status); + LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d", + output, + samplingRate, + format, + channels, + status); + + mHardwareStatus = AUDIO_HW_IDLE; + if (output != 0) { + if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format != AudioSystem::PCM_16_BIT) || + (channels != AudioSystem::CHANNEL_OUT_STEREO)) { + thread = new DirectOutputThread(this, output, ++mNextThreadId); + LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread); + } else { + thread = new MixerThread(this, output, ++mNextThreadId); + LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread); + +#ifdef LVMX + unsigned bitsPerSample = + (format == AudioSystem::PCM_16_BIT) ? 16 : + ((format == AudioSystem::PCM_8_BIT) ? 8 : 0); + unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1; + int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id()); + + LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount); + LifeVibes::setDevice(audioOutputType, *pDevices); +#endif + + } + mPlaybackThreads.add(mNextThreadId, thread); + + if (pSamplingRate) *pSamplingRate = samplingRate; + if (pFormat) *pFormat = format; + if (pChannels) *pChannels = channels; + if (pLatencyMs) *pLatencyMs = thread->latency(); + + return mNextThreadId; + } + + return 0; +} + +int AudioFlinger::openDuplicateOutput(int output1, int output2) +{ + Mutex::Autolock _l(mLock); + MixerThread *thread1 = checkMixerThread_l(output1); + MixerThread *thread2 = checkMixerThread_l(output2); + + if (thread1 == NULL || thread2 == NULL) { + LOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2); + return 0; + } + + + DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); + thread->addOutputTrack(thread2); + mPlaybackThreads.add(mNextThreadId, thread); + return mNextThreadId; +} + +status_t AudioFlinger::closeOutput(int output) +{ + // keep strong reference on the playback thread so that + // it is not destroyed while exit() is executed + sp <PlaybackThread> thread; + { + Mutex::Autolock _l(mLock); + thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("closeOutput() %d", output); + + if (thread->type() == PlaybackThread::MIXER) { + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->type() == PlaybackThread::DUPLICATING) { + DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get(); + dupThread->removeOutputTrack((MixerThread *)thread.get()); + } + } + } + void *param2 = 0; + audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2); + mPlaybackThreads.removeItem(output); + } + thread->exit(); + + if (thread->type() != PlaybackThread::DUPLICATING) { + mAudioHardware->closeOutputStream(thread->getOutput()); + } + return NO_ERROR; +} + +status_t AudioFlinger::suspendOutput(int output) +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("suspendOutput() %d", output); + thread->suspend(); + + return NO_ERROR; +} + +status_t AudioFlinger::restoreOutput(int output) +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("restoreOutput() %d", output); + + thread->restore(); + + return NO_ERROR; +} + +int AudioFlinger::openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics) +{ + status_t status; + RecordThread *thread = NULL; + uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; + uint32_t format = pFormat ? *pFormat : 0; + uint32_t channels = pChannels ? *pChannels : 0; + uint32_t reqSamplingRate = samplingRate; + uint32_t reqFormat = format; + uint32_t reqChannels = channels; + + if (pDevices == NULL || *pDevices == 0) { + return 0; + } + Mutex::Autolock _l(mLock); + + AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status, + (AudioSystem::audio_in_acoustics)acoustics); + LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d", + input, + samplingRate, + format, + channels, + acoustics, + status); + + // If the input could not be opened with the requested parameters and we can handle the conversion internally, + // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo + // or stereo to mono conversions on 16 bit PCM inputs. + if (input == 0 && status == BAD_VALUE && + reqFormat == format && format == AudioSystem::PCM_16_BIT && + (samplingRate <= 2 * reqSamplingRate) && + (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) { + LOGV("openInput() reopening with proposed sampling rate and channels"); + input = mAudioHardware->openInputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status, + (AudioSystem::audio_in_acoustics)acoustics); + } + + if (input != 0) { + // Start record thread + thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId); + mRecordThreads.add(mNextThreadId, thread); + LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread); + if (pSamplingRate) *pSamplingRate = reqSamplingRate; + if (pFormat) *pFormat = format; + if (pChannels) *pChannels = reqChannels; + + input->standby(); + + return mNextThreadId; + } + + return 0; +} + +status_t AudioFlinger::closeInput(int input) +{ + // keep strong reference on the record thread so that + // it is not destroyed while exit() is executed + sp <RecordThread> thread; + { + Mutex::Autolock _l(mLock); + thread = checkRecordThread_l(input); + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("closeInput() %d", input); + void *param2 = 0; + audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2); + mRecordThreads.removeItem(input); + } + thread->exit(); + + mAudioHardware->closeInputStream(thread->getInput()); + + return NO_ERROR; +} + +status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) +{ + Mutex::Autolock _l(mLock); + MixerThread *dstThread = checkMixerThread_l(output); + if (dstThread == NULL) { + LOGW("setStreamOutput() bad output id %d", output); + return BAD_VALUE; + } + + LOGV("setStreamOutput() stream %d to output %d", stream, output); + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); + if (thread != dstThread && + thread->type() != PlaybackThread::DIRECT) { + MixerThread *srcThread = (MixerThread *)thread; + SortedVector < sp<MixerThread::Track> > tracks; + SortedVector < wp<MixerThread::Track> > activeTracks; + srcThread->getTracks(tracks, activeTracks, stream); + if (tracks.size()) { + dstThread->putTracks(tracks, activeTracks); + } + } + } + + dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream); + + return NO_ERROR; +} + +// checkPlaybackThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const +{ + PlaybackThread *thread = NULL; + if (mPlaybackThreads.indexOfKey(output) >= 0) { + thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get(); + } + return thread; +} + +// checkMixerThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(int output) const +{ + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread != NULL) { + if (thread->type() == PlaybackThread::DIRECT) { + thread = NULL; + } + } + return (MixerThread *)thread; +} + +// checkRecordThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const +{ + RecordThread *thread = NULL; + if (mRecordThreads.indexOfKey(input) >= 0) { + thread = (RecordThread *)mRecordThreads.valueFor(input).get(); + } + return thread; +} + +// ---------------------------------------------------------------------------- + +status_t AudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioFlinger::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +void AudioFlinger::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_flinger"), new AudioFlinger()); +} + +}; // namespace android diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h new file mode 100644 index 0000000..739ec33 --- /dev/null +++ b/services/audioflinger/AudioFlinger.h @@ -0,0 +1,807 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.h +** +** 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_FLINGER_H +#define ANDROID_AUDIO_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> +#include <limits.h> + +#include <media/IAudioFlinger.h> +#include <media/IAudioFlingerClient.h> +#include <media/IAudioTrack.h> +#include <media/IAudioRecord.h> +#include <media/AudioTrack.h> + +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/threads.h> +#include <binder/MemoryDealer.h> +#include <utils/SortedVector.h> +#include <utils/Vector.h> + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include "AudioBufferProvider.h" + +namespace android { + +class audio_track_cblk_t; +class AudioMixer; +class AudioBuffer; +class AudioResampler; + + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + + +// ---------------------------------------------------------------------------- + +static const nsecs_t kStandbyTimeInNsecs = seconds(3); + +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +{ +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // IAudioFlinger interface + virtual sp<IAudioTrack> createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + int output, + status_t *status); + + virtual uint32_t sampleRate(int output) const; + virtual int channelCount(int output) const; + virtual int format(int output) const; + virtual size_t frameCount(int output) const; + virtual uint32_t latency(int output) const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value, int output); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream, int output) const; + virtual bool streamMute(int stream) const; + + virtual status_t setMode(int mode); + + virtual status_t setMicMute(bool state); + virtual bool getMicMute() const; + + virtual bool isStreamActive(int stream) const; + + virtual status_t setParameters(int ioHandle, const String8& keyValuePairs); + virtual String8 getParameters(int ioHandle, const String8& keys); + + virtual void registerClient(const sp<IAudioFlingerClient>& client); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + virtual unsigned int getInputFramesLost(int ioHandle); + + virtual int openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + uint32_t flags); + + virtual int openDuplicateOutput(int output1, int output2); + + virtual status_t closeOutput(int output); + + virtual status_t suspendOutput(int output); + + virtual status_t restoreOutput(int output); + + virtual int openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics); + + virtual status_t closeInput(int input); + + virtual status_t setStreamOutput(uint32_t stream, int output); + + virtual status_t setVoiceVolume(float volume); + + virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output); + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + enum hardware_call_state { + AUDIO_HW_IDLE = 0, + AUDIO_HW_INIT, + AUDIO_HW_OUTPUT_OPEN, + AUDIO_HW_OUTPUT_CLOSE, + AUDIO_HW_INPUT_OPEN, + AUDIO_HW_INPUT_CLOSE, + AUDIO_HW_STANDBY, + AUDIO_HW_SET_MASTER_VOLUME, + AUDIO_HW_GET_ROUTING, + AUDIO_HW_SET_ROUTING, + AUDIO_HW_GET_MODE, + AUDIO_HW_SET_MODE, + AUDIO_HW_GET_MIC_MUTE, + AUDIO_HW_SET_MIC_MUTE, + AUDIO_SET_VOICE_VOLUME, + AUDIO_SET_PARAMETER, + }; + + // record interface + virtual sp<IAudioRecord> openRecord( + pid_t pid, + int input, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + +private: + AudioFlinger(); + virtual ~AudioFlinger(); + + + // Internal dump utilites. + status_t dumpPermissionDenial(int fd, const Vector<String16>& args); + status_t dumpClients(int fd, const Vector<String16>& args); + status_t dumpInternals(int fd, const Vector<String16>& args); + + // --- Client --- + class Client : public RefBase { + public: + Client(const sp<AudioFlinger>& audioFlinger, pid_t pid); + virtual ~Client(); + const sp<MemoryDealer>& heap() const; + pid_t pid() const { return mPid; } + sp<AudioFlinger> audioFlinger() { return mAudioFlinger; } + + private: + Client(const Client&); + Client& operator = (const Client&); + sp<AudioFlinger> mAudioFlinger; + sp<MemoryDealer> mMemoryDealer; + pid_t mPid; + }; + + + class TrackHandle; + class RecordHandle; + class RecordThread; + class PlaybackThread; + class MixerThread; + class DirectOutputThread; + class DuplicatingThread; + class Track; + class RecordTrack; + + class ThreadBase : public Thread { + public: + ThreadBase (const sp<AudioFlinger>& audioFlinger, int id); + virtual ~ThreadBase(); + + status_t dumpBase(int fd, const Vector<String16>& args); + + // base for record and playback + class TrackBase : public AudioBufferProvider, public RefBase { + + public: + enum track_state { + IDLE, + TERMINATED, + STOPPED, + RESUMING, + ACTIVE, + PAUSING, + PAUSED + }; + + enum track_flags { + STEPSERVER_FAILED = 0x01, // StepServer could not acquire cblk->lock mutex + SYSTEM_FLAGS_MASK = 0x0000ffffUL, + // The upper 16 bits are used for track-specific flags. + }; + + TrackBase(const wp<ThreadBase>& thread, + const sp<Client>& client, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer); + ~TrackBase(); + + virtual status_t start() = 0; + virtual void stop() = 0; + sp<IMemory> getCblk() const; + audio_track_cblk_t* cblk() const { return mCblk; } + + protected: + friend class ThreadBase; + friend class RecordHandle; + friend class PlaybackThread; + friend class RecordThread; + friend class MixerThread; + friend class DirectOutputThread; + + TrackBase(const TrackBase&); + TrackBase& operator = (const TrackBase&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + int format() const { + return mFormat; + } + + int channelCount() const ; + + int sampleRate() const; + + void* getBuffer(uint32_t offset, uint32_t frames) const; + + bool isStopped() const { + return mState == STOPPED; + } + + bool isTerminated() const { + return mState == TERMINATED; + } + + bool step(); + void reset(); + + wp<ThreadBase> mThread; + sp<Client> mClient; + sp<IMemory> mCblkMemory; + audio_track_cblk_t* mCblk; + void* mBuffer; + void* mBufferEnd; + uint32_t mFrameCount; + // we don't really need a lock for these + int mState; + int mClientTid; + uint8_t mFormat; + uint32_t mFlags; + }; + + class ConfigEvent { + public: + ConfigEvent() : mEvent(0), mParam(0) {} + + int mEvent; + int mParam; + }; + + uint32_t sampleRate() const; + int channelCount() const; + int format() const; + size_t frameCount() const; + void wakeUp() { mWaitWorkCV.broadcast(); } + void exit(); + virtual bool checkForNewParameters_l() = 0; + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys) = 0; + virtual void audioConfigChanged(int event, int param = 0) = 0; + void sendConfigEvent(int event, int param = 0); + void sendConfigEvent_l(int event, int param = 0); + void processConfigEvents(); + int id() const { return mId;} + bool standby() { return mStandby; } + + mutable Mutex mLock; + + protected: + + friend class Track; + friend class TrackBase; + friend class PlaybackThread; + friend class MixerThread; + friend class DirectOutputThread; + friend class DuplicatingThread; + friend class RecordThread; + friend class RecordTrack; + + Condition mWaitWorkCV; + sp<AudioFlinger> mAudioFlinger; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + uint32_t mFrameSize; + Condition mParamCond; + Vector<String8> mNewParameters; + status_t mParamStatus; + Vector<ConfigEvent *> mConfigEvents; + bool mStandby; + int mId; + bool mExiting; + }; + + // --- PlaybackThread --- + class PlaybackThread : public ThreadBase { + public: + + enum type { + MIXER, + DIRECT, + DUPLICATING + }; + + enum mixer_state { + MIXER_IDLE, + MIXER_TRACKS_ENABLED, + MIXER_TRACKS_READY + }; + + // playback track + class Track : public TrackBase { + public: + Track( const wp<ThreadBase>& thread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer); + ~Track(); + + void dump(char* buffer, size_t size); + virtual status_t start(); + virtual void stop(); + void pause(); + + void flush(); + void destroy(); + void mute(bool); + void setVolume(float left, float right); + int name() const { + return mName; + } + + int type() const { + return mStreamType; + } + + + protected: + friend class ThreadBase; + friend class AudioFlinger; + friend class TrackHandle; + friend class PlaybackThread; + friend class MixerThread; + friend class DirectOutputThread; + + Track(const Track&); + Track& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + bool isMuted() { return mMute; } + bool isPausing() const { + return mState == PAUSING; + } + bool isPaused() const { + return mState == PAUSED; + } + bool isReady() const; + void setPaused() { mState = PAUSED; } + void reset(); + + bool isOutputTrack() const { + return (mStreamType == AudioSystem::NUM_STREAM_TYPES); + } + + // we don't really need a lock for these + float mVolume[2]; + volatile bool mMute; + // FILLED state is used for suppressing volume ramp at begin of playing + enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; + mutable uint8_t mFillingUpStatus; + int8_t mRetryCount; + sp<IMemory> mSharedBuffer; + bool mResetDone; + int mStreamType; + int mName; + }; // end of Track + + + // playback track + class OutputTrack : public Track { + public: + + class Buffer: public AudioBufferProvider::Buffer { + public: + int16_t *mBuffer; + }; + + OutputTrack( const wp<ThreadBase>& thread, + DuplicatingThread *sourceThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount); + ~OutputTrack(); + + virtual status_t start(); + virtual void stop(); + bool write(int16_t* data, uint32_t frames); + bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; } + bool isActive() { return mActive; } + wp<ThreadBase>& thread() { return mThread; } + + private: + + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs); + void clearBufferQueue(); + + // Maximum number of pending buffers allocated by OutputTrack::write() + static const uint8_t kMaxOverFlowBuffers = 10; + + Vector < Buffer* > mBufferQueue; + AudioBufferProvider::Buffer mOutBuffer; + bool mActive; + DuplicatingThread* mSourceThread; + }; // end of OutputTrack + + PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + virtual ~PlaybackThread(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // Thread virtuals + virtual status_t readyToRun(); + virtual void onFirstRef(); + + virtual uint32_t latency() const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + bool isStreamActive(int stream) const; + + sp<Track> createTrack_l( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status); + + AudioStreamOut* getOutput() { return mOutput; } + + virtual int type() const { return mType; } + void suspend() { mSuspended++; } + void restore() { if (mSuspended) mSuspended--; } + bool isSuspended() { return (mSuspended != 0); } + virtual String8 getParameters(const String8& keys); + virtual void audioConfigChanged(int event, int param = 0); + virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); + + struct stream_type_t { + stream_type_t() + : volume(1.0f), + mute(false) + { + } + float volume; + bool mute; + }; + + protected: + int mType; + int16_t* mMixBuffer; + int mSuspended; + int mBytesWritten; + bool mMasterMute; + SortedVector< wp<Track> > mActiveTracks; + + virtual int getTrackName_l() = 0; + virtual void deleteTrackName_l(int name) = 0; + virtual uint32_t activeSleepTimeUs() = 0; + virtual uint32_t idleSleepTimeUs() = 0; + + private: + + friend class AudioFlinger; + friend class OutputTrack; + friend class Track; + friend class TrackBase; + friend class MixerThread; + friend class DirectOutputThread; + friend class DuplicatingThread; + + PlaybackThread(const Client&); + PlaybackThread& operator = (const PlaybackThread&); + + status_t addTrack_l(const sp<Track>& track); + void destroyTrack_l(const sp<Track>& track); + + void readOutputParameters(); + + virtual status_t dumpInternals(int fd, const Vector<String16>& args); + status_t dumpTracks(int fd, const Vector<String16>& args); + + SortedVector< sp<Track> > mTracks; + // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1]; + AudioStreamOut* mOutput; + float mMasterVolume; + nsecs_t mLastWriteTime; + int mNumWrites; + int mNumDelayedWrites; + bool mInWrite; + }; + + class MixerThread : public PlaybackThread { + public: + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + virtual ~MixerThread(); + + // Thread virtuals + virtual bool threadLoop(); + + void getTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks, + int streamType); + void putTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + virtual bool checkForNewParameters_l(); + virtual status_t dumpInternals(int fd, const Vector<String16>& args); + + protected: + uint32_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove); + virtual int getTrackName_l(); + virtual void deleteTrackName_l(int name); + virtual uint32_t activeSleepTimeUs(); + virtual uint32_t idleSleepTimeUs(); + + AudioMixer* mAudioMixer; + }; + + class DirectOutputThread : public PlaybackThread { + public: + + DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + ~DirectOutputThread(); + + // Thread virtuals + virtual bool threadLoop(); + + virtual bool checkForNewParameters_l(); + + protected: + virtual int getTrackName_l(); + virtual void deleteTrackName_l(int name); + virtual uint32_t activeSleepTimeUs(); + virtual uint32_t idleSleepTimeUs(); + + private: + float mLeftVolume; + float mRightVolume; + }; + + class DuplicatingThread : public MixerThread { + public: + DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread, int id); + ~DuplicatingThread(); + + // Thread virtuals + virtual bool threadLoop(); + void addOutputTrack(MixerThread* thread); + void removeOutputTrack(MixerThread* thread); + uint32_t waitTimeMs() { return mWaitTimeMs; } + protected: + virtual uint32_t activeSleepTimeUs(); + + private: + bool outputsReady(SortedVector< sp<OutputTrack> > &outputTracks); + void updateWaitTime(); + + SortedVector < sp<OutputTrack> > mOutputTracks; + uint32_t mWaitTimeMs; + }; + + PlaybackThread *checkPlaybackThread_l(int output) const; + MixerThread *checkMixerThread_l(int output) const; + RecordThread *checkRecordThread_l(int input) const; + float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; } + void audioConfigChanged_l(int event, int ioHandle, void *param2); + + friend class AudioBuffer; + + class TrackHandle : public android::BnAudioTrack { + public: + TrackHandle(const sp<PlaybackThread::Track>& track); + virtual ~TrackHandle(); + virtual status_t start(); + virtual void stop(); + virtual void flush(); + virtual void mute(bool); + virtual void pause(); + virtual void setVolume(float left, float right); + virtual sp<IMemory> getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp<PlaybackThread::Track> mTrack; + }; + + friend class Client; + friend class PlaybackThread::Track; + + + void removeClient_l(pid_t pid); + + + // record thread + class RecordThread : public ThreadBase, public AudioBufferProvider + { + public: + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack(const wp<ThreadBase>& thread, + const sp<Client>& client, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags); + ~RecordTrack(); + + virtual status_t start(); + virtual void stop(); + + bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + + void dump(char* buffer, size_t size); + private: + friend class AudioFlinger; + friend class RecordThread; + + RecordTrack(const RecordTrack&); + RecordTrack& operator = (const RecordTrack&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; + }; + + + RecordThread(const sp<AudioFlinger>& audioFlinger, + AudioStreamIn *input, + uint32_t sampleRate, + uint32_t channels, + int id); + ~RecordThread(); + + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef(); + + status_t start(RecordTrack* recordTrack); + void stop(RecordTrack* recordTrack); + status_t dump(int fd, const Vector<String16>& args); + AudioStreamIn* getInput() { return mInput; } + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + virtual bool checkForNewParameters_l(); + virtual String8 getParameters(const String8& keys); + virtual void audioConfigChanged(int event, int param = 0); + void readInputParameters(); + virtual unsigned int getInputFramesLost(); + + private: + RecordThread(); + AudioStreamIn *mInput; + sp<RecordTrack> mActiveTrack; + Condition mStartStopCond; + AudioResampler *mResampler; + int32_t *mRsmpOutBuffer; + int16_t *mRsmpInBuffer; + size_t mRsmpInIndex; + size_t mInputBytes; + int mReqChannelCount; + uint32_t mReqSampleRate; + ssize_t mBytesRead; + }; + + class RecordHandle : public android::BnAudioRecord { + public: + RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); + virtual ~RecordHandle(); + virtual status_t start(); + virtual void stop(); + virtual sp<IMemory> getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp<RecordThread::RecordTrack> mRecordTrack; + }; + + friend class RecordThread; + friend class PlaybackThread; + + + mutable Mutex mLock; + + DefaultKeyedVector< pid_t, wp<Client> > mClients; + + mutable Mutex mHardwareLock; + AudioHardwareInterface* mAudioHardware; + mutable int mHardwareStatus; + + + DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads; + PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; + float mMasterVolume; + bool mMasterMute; + + DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads; + + SortedVector< sp<IBinder> > mNotificationClients; + int mNextThreadId; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_FLINGER_H diff --git a/services/audioflinger/AudioHardwareGeneric.cpp b/services/audioflinger/AudioHardwareGeneric.cpp new file mode 100644 index 0000000..d63c031 --- /dev/null +++ b/services/audioflinger/AudioHardwareGeneric.cpp @@ -0,0 +1,411 @@ +/* +** +** 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. +*/ + +#include <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sched.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define LOG_TAG "AudioHardware" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareGeneric.h" +#include <media/AudioRecord.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static char const * const kAudioDeviceName = "/dev/eac"; + +// ---------------------------------------------------------------------------- + +AudioHardwareGeneric::AudioHardwareGeneric() + : mOutput(0), mInput(0), mFd(-1), mMicMute(false) +{ + mFd = ::open(kAudioDeviceName, O_RDWR); +} + +AudioHardwareGeneric::~AudioHardwareGeneric() +{ + if (mFd >= 0) ::close(mFd); + closeOutputStream((AudioStreamOut *)mOutput); + closeInputStream((AudioStreamIn *)mInput); +} + +status_t AudioHardwareGeneric::initCheck() +{ + if (mFd >= 0) { + if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR) + return NO_ERROR; + } + return NO_INIT; +} + +AudioStreamOut* AudioHardwareGeneric::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AutoMutex lock(mLock); + + // only one output stream allowed + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); + status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + return mOutput; +} + +void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) { + if (mOutput && out == mOutput) { + delete mOutput; + mOutput = 0; + } +} + +AudioStreamIn* AudioHardwareGeneric::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + return 0; + } + + AutoMutex lock(mLock); + + // only one input stream allowed + if (mInput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamInGeneric* in = new AudioStreamInGeneric(); + status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mInput = in; + } else { + delete in; + } + return mInput; +} + +void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) { + if (mInput && in == mInput) { + delete mInput; + mInput = 0; + } +} + +status_t AudioHardwareGeneric::setVoiceVolume(float v) +{ + // Implement: set voice volume + return NO_ERROR; +} + +status_t AudioHardwareGeneric::setMasterVolume(float v) +{ + // Implement: set master volume + // return error - software mixer will handle it + return INVALID_OPERATION; +} + +status_t AudioHardwareGeneric::setMicMute(bool state) +{ + mMicMute = state; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareGeneric::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + if (mInput) { + mInput->dump(fd, args); + } + if (mOutput) { + mOutput->dump(fd, args); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutGeneric::set( + AudioHardwareGeneric *hw, + int fd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())) { + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mAudioHardware = hw; + mFd = fd; + mDevice = devices; + return NO_ERROR; +} + +AudioStreamOutGeneric::~AudioStreamOutGeneric() +{ +} + +ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) +{ + Mutex::Autolock _l(mLock); + return ssize_t(::write(mFd, buffer, bytes)); +} + +status_t AudioStreamOutGeneric::standby() +{ + // Implement: audio hardware to standby mode + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + LOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamOutGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + +status_t AudioStreamOutGeneric::getRenderPosition(uint32_t *dspFrames) +{ + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +// record functions +status_t AudioStreamInGeneric::set( + AudioHardwareGeneric *hw, + int fd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics) +{ + if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE; + LOGV("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate); + // check values + if ((*pFormat != format()) || + (*pChannels != channels()) || + (*pRate != sampleRate())) { + LOGE("Error opening input channel"); + *pFormat = format(); + *pChannels = channels(); + *pRate = sampleRate(); + return BAD_VALUE; + } + + mAudioHardware = hw; + mFd = fd; + mDevice = devices; + return NO_ERROR; +} + +AudioStreamInGeneric::~AudioStreamInGeneric() +{ +} + +ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) +{ + AutoMutex lock(mLock); + if (mFd < 0) { + LOGE("Attempt to read from unopened device"); + return NO_INIT; + } + return ::read(mFd, buffer, bytes); +} + +status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + LOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamInGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/audioflinger/AudioHardwareGeneric.h b/services/audioflinger/AudioHardwareGeneric.h new file mode 100644 index 0000000..aa4e78d --- /dev/null +++ b/services/audioflinger/AudioHardwareGeneric.h @@ -0,0 +1,151 @@ +/* +** +** 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_HARDWARE_GENERIC_H +#define ANDROID_AUDIO_HARDWARE_GENERIC_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioHardwareGeneric; + +class AudioStreamOutGeneric : public AudioStreamOut { +public: + AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamOutGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 20; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; + uint32_t mDevice; +}; + +class AudioStreamInGeneric : public AudioStreamIn { +public: + AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamInGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics); + + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return INVALID_OPERATION; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const { return 0; } + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; + uint32_t mDevice; +}; + + +class AudioHardwareGeneric : public AudioHardwareBase +{ +public: + AudioHardwareGeneric(); + virtual ~AudioHardwareGeneric(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + + void closeOutputStream(AudioStreamOutGeneric* out); + void closeInputStream(AudioStreamInGeneric* in); +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + status_t dumpInternals(int fd, const Vector<String16>& args); + + Mutex mLock; + AudioStreamOutGeneric *mOutput; + AudioStreamInGeneric *mInput; + int mFd; + bool mMicMute; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H diff --git a/services/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp new file mode 100644 index 0000000..9a4a7f9 --- /dev/null +++ b/services/audioflinger/AudioHardwareInterface.cpp @@ -0,0 +1,182 @@ +/* +** +** 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. +*/ + +#include <cutils/properties.h> +#include <string.h> +#include <unistd.h> +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioHardwareInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" +#include "AudioHardwareGeneric.h" +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + +#ifdef ENABLE_AUDIO_DUMP +#include "AudioDumpInterface.h" +#endif + + +// change to 1 to log routing calls +#define LOG_ROUTING_CALLS 1 + +namespace android { + +#if LOG_ROUTING_CALLS +static const char* routingModeStrings[] = +{ + "OUT OF RANGE", + "INVALID", + "CURRENT", + "NORMAL", + "RINGTONE", + "IN_CALL" +}; + +static const char* routeNone = "NONE"; + +static const char* displayMode(int mode) +{ + if ((mode < -2) || (mode > 2)) + return routingModeStrings[0]; + return routingModeStrings[mode+3]; +} +#endif + +// ---------------------------------------------------------------------------- + +AudioHardwareInterface* AudioHardwareInterface::create() +{ + /* + * FIXME: This code needs to instantiate the correct audio device + * interface. For now - we use compile-time switches. + */ + AudioHardwareInterface* hw = 0; + char value[PROPERTY_VALUE_MAX]; + +#ifdef GENERIC_AUDIO + hw = new AudioHardwareGeneric(); +#else + // if running in emulation - use the emulator driver + if (property_get("ro.kernel.qemu", value, 0)) { + LOGD("Running in emulation - using generic audio driver"); + hw = new AudioHardwareGeneric(); + } + else { + LOGV("Creating Vendor Specific AudioHardware"); + hw = createAudioHardware(); + } +#endif + if (hw->initCheck() != NO_ERROR) { + LOGW("Using stubbed audio hardware. No sound will be produced."); + delete hw; + hw = new AudioHardwareStub(); + } + +#ifdef WITH_A2DP + hw = new A2dpAudioInterface(hw); +#endif + +#ifdef ENABLE_AUDIO_DUMP + // This code adds a record of buffers in a file to write calls made by AudioFlinger. + // It replaces the current AudioHardwareInterface object by an intermediate one which + // will record buffers in a file (after sending them to hardware) for testing purpose. + // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP. + // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file. + LOGV("opening PCM dump interface"); + hw = new AudioDumpInterface(hw); // replace interface +#endif + return hw; +} + +AudioStreamOut::~AudioStreamOut() +{ +} + +AudioStreamIn::~AudioStreamIn() {} + +AudioHardwareBase::AudioHardwareBase() +{ + mMode = 0; +} + +status_t AudioHardwareBase::setMode(int mode) +{ +#if LOG_ROUTING_CALLS + LOGD("setMode(%s)", displayMode(mode)); +#endif + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + if (mMode == mode) + return ALREADY_EXISTS; + mMode = mode; + return NO_ERROR; +} + +// default implementation +status_t AudioHardwareBase::setParameters(const String8& keyValuePairs) +{ + return NO_ERROR; +} + +// default implementation +String8 AudioHardwareBase::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +// default implementation +size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + if (sampleRate != 8000) { + LOGW("getInputBufferSize bad sampling rate: %d", sampleRate); + return 0; + } + if (format != AudioSystem::PCM_16_BIT) { + LOGW("getInputBufferSize bad format: %d", format); + return 0; + } + if (channelCount != 1) { + LOGW("getInputBufferSize bad channel count: %d", channelCount); + return 0; + } + + return 320; +} + +status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); + result.append(buffer); + ::write(fd, result.string(), result.size()); + dump(fd, args); // Dump the state of the concrete child. + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/audioflinger/AudioHardwareStub.cpp b/services/audioflinger/AudioHardwareStub.cpp new file mode 100644 index 0000000..d481150 --- /dev/null +++ b/services/audioflinger/AudioHardwareStub.cpp @@ -0,0 +1,209 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.cpp +** +** 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. +*/ + +#include <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <unistd.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" +#include <media/AudioRecord.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioHardwareStub::AudioHardwareStub() : mMicMute(false) +{ +} + +AudioHardwareStub::~AudioHardwareStub() +{ +} + +status_t AudioHardwareStub::initCheck() +{ + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareStub::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AudioStreamOutStub* out = new AudioStreamOutStub(); + status_t lStatus = out->set(format, channels, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return out; + delete out; + return 0; +} + +void AudioHardwareStub::closeOutputStream(AudioStreamOut* out) +{ + delete out; +} + +AudioStreamIn* AudioHardwareStub::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + return 0; + } + + AudioStreamInStub* in = new AudioStreamInStub(); + status_t lStatus = in->set(format, channels, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return in; + delete in; + return 0; +} + +void AudioHardwareStub::closeInputStream(AudioStreamIn* in) +{ + delete in; +} + +status_t AudioHardwareStub::setVoiceVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::setMasterVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareStub::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + + return NO_ERROR; +} + +ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) +{ + // fake timing for audio output + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); + return bytes; +} + +status_t AudioStreamOutStub::standby() +{ + return NO_ERROR; +} + +status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +String8 AudioStreamOutStub::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames) +{ + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics) +{ + return NO_ERROR; +} + +ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) +{ + // fake timing for audio input + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); + memset(buffer, 0, bytes); + return bytes; +} + +status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInStub::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +String8 AudioStreamInStub::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/audioflinger/AudioHardwareStub.h b/services/audioflinger/AudioHardwareStub.h new file mode 100644 index 0000000..06a29de --- /dev/null +++ b/services/audioflinger/AudioHardwareStub.h @@ -0,0 +1,106 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.h +** +** 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_HARDWARE_STUB_H +#define ANDROID_AUDIO_HARDWARE_STUB_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioStreamOutStub : public AudioStreamOut { +public: + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate); + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } + virtual status_t setVolume(float left, float right) { return NO_ERROR; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys); + virtual status_t getRenderPosition(uint32_t *dspFrames); +}; + +class AudioStreamInStub : public AudioStreamIn { +public: + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics); + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return NO_ERROR; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const { return 0; } +}; + +class AudioHardwareStub : public AudioHardwareBase +{ +public: + AudioHardwareStub(); + virtual ~AudioHardwareStub(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } + virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); + +protected: + virtual status_t dump(int fd, const Vector<String16>& args); + + bool mMicMute; +private: + status_t dumpInternals(int fd, const Vector<String16>& args); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_STUB_H diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp new file mode 100644 index 0000000..19a442a --- /dev/null +++ b/services/audioflinger/AudioMixer.cpp @@ -0,0 +1,915 @@ +/* //device/include/server/AudioFlinger/AudioMixer.cpp +** +** 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 "AudioMixer" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "AudioMixer.h" + +namespace android { +// ---------------------------------------------------------------------------- + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +// ---------------------------------------------------------------------------- + +AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) + : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate) +{ + mState.enabledTracks= 0; + mState.needsChanged = 0; + mState.frameCount = frameCount; + mState.outputTemp = 0; + mState.resampleTemp = 0; + mState.hook = process__nop; + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + t->needs = 0; + t->volume[0] = UNITY_GAIN; + t->volume[1] = UNITY_GAIN; + t->volumeInc[0] = 0; + t->volumeInc[1] = 0; + t->channelCount = 2; + t->enabled = 0; + t->format = 16; + t->buffer.raw = 0; + t->bufferProvider = 0; + t->hook = 0; + t->resampler = 0; + t->sampleRate = mSampleRate; + t->in = 0; + t++; + } +} + + AudioMixer::~AudioMixer() + { + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + delete t->resampler; + t++; + } + delete [] mState.outputTemp; + delete [] mState.resampleTemp; + } + + int AudioMixer::getTrackName() + { + uint32_t names = mTrackNames; + uint32_t mask = 1; + int n = 0; + while (names & mask) { + mask <<= 1; + n++; + } + if (mask) { + LOGV("add track (%d)", n); + mTrackNames |= mask; + return TRACK0 + n; + } + return -1; + } + + void AudioMixer::invalidateState(uint32_t mask) + { + if (mask) { + mState.needsChanged |= mask; + mState.hook = process__validate; + } + } + + void AudioMixer::deleteTrackName(int name) + { + name -= TRACK0; + if (uint32_t(name) < MAX_NUM_TRACKS) { + LOGV("deleteTrackName(%d)", name); + track_t& track(mState.tracks[ name ]); + if (track.enabled != 0) { + track.enabled = 0; + invalidateState(1<<name); + } + if (track.resampler) { + // delete the resampler + delete track.resampler; + track.resampler = 0; + track.sampleRate = mSampleRate; + invalidateState(1<<name); + } + track.volumeInc[0] = 0; + track.volumeInc[1] = 0; + mTrackNames &= ~(1<<name); + } + } + +status_t AudioMixer::enable(int name) +{ + switch (name) { + case MIXING: { + if (mState.tracks[ mActiveTrack ].enabled != 1) { + mState.tracks[ mActiveTrack ].enabled = 1; + LOGV("enable(%d)", mActiveTrack); + invalidateState(1<<mActiveTrack); + } + } break; + default: + return NAME_NOT_FOUND; + } + return NO_ERROR; +} + +status_t AudioMixer::disable(int name) +{ + switch (name) { + case MIXING: { + if (mState.tracks[ mActiveTrack ].enabled != 0) { + mState.tracks[ mActiveTrack ].enabled = 0; + LOGV("disable(%d)", mActiveTrack); + invalidateState(1<<mActiveTrack); + } + } break; + default: + return NAME_NOT_FOUND; + } + return NO_ERROR; +} + +status_t AudioMixer::setActiveTrack(int track) +{ + if (uint32_t(track-TRACK0) >= MAX_NUM_TRACKS) { + return BAD_VALUE; + } + mActiveTrack = track - TRACK0; + return NO_ERROR; +} + +status_t AudioMixer::setParameter(int target, int name, int value) +{ + switch (target) { + case TRACK: + if (name == CHANNEL_COUNT) { + if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) { + if (mState.tracks[ mActiveTrack ].channelCount != value) { + mState.tracks[ mActiveTrack ].channelCount = value; + LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + } + break; + case RESAMPLE: + if (name == SAMPLE_RATE) { + if (value > 0) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.setResampler(uint32_t(value), mSampleRate)) { + LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)", + uint32_t(value)); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + } + break; + case RAMP_VOLUME: + case VOLUME: + if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.volume[name-VOLUME0] != value) { + track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16; + track.volume[name-VOLUME0] = value; + if (target == VOLUME) { + track.prevVolume[name-VOLUME0] = value << 16; + track.volumeInc[name-VOLUME0] = 0; + } else { + int32_t d = (value<<16) - track.prevVolume[name-VOLUME0]; + int32_t volInc = d / int32_t(mState.frameCount); + track.volumeInc[name-VOLUME0] = volInc; + if (volInc == 0) { + track.prevVolume[name-VOLUME0] = value << 16; + } + } + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + break; + } + return BAD_VALUE; +} + +bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) +{ + if (value!=devSampleRate || resampler) { + if (sampleRate != value) { + sampleRate = value; + if (resampler == 0) { + resampler = AudioResampler::create( + format, channelCount, devSampleRate); + } + return true; + } + } + return false; +} + +bool AudioMixer::track_t::doesResample() const +{ + return resampler != 0; +} + +inline +void AudioMixer::track_t::adjustVolumeRamp() +{ + for (int i=0 ; i<2 ; i++) { + if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || + ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { + volumeInc[i] = 0; + prevVolume[i] = volume[i]<<16; + } + } +} + + +status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) +{ + mState.tracks[ mActiveTrack ].bufferProvider = buffer; + return NO_ERROR; +} + + + +void AudioMixer::process(void* output) +{ + mState.hook(&mState, output); +} + + +void AudioMixer::process__validate(state_t* state, void* output) +{ + LOGW_IF(!state->needsChanged, + "in process__validate() but nothing's invalid"); + + uint32_t changed = state->needsChanged; + state->needsChanged = 0; // clear the validation flag + + // recompute which tracks are enabled / disabled + uint32_t enabled = 0; + uint32_t disabled = 0; + while (changed) { + const int i = 31 - __builtin_clz(changed); + const uint32_t mask = 1<<i; + changed &= ~mask; + track_t& t = state->tracks[i]; + (t.enabled ? enabled : disabled) |= mask; + } + state->enabledTracks &= ~disabled; + state->enabledTracks |= enabled; + + // compute everything we need... + int countActiveTracks = 0; + int all16BitsStereoNoResample = 1; + int resampling = 0; + int volumeRamp = 0; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + + countActiveTracks++; + track_t& t = state->tracks[i]; + uint32_t n = 0; + n |= NEEDS_CHANNEL_1 + t.channelCount - 1; + n |= NEEDS_FORMAT_16; + n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; + + if (t.volumeInc[0]|t.volumeInc[1]) { + volumeRamp = 1; + } else if (!t.doesResample() && t.volumeRL == 0) { + n |= NEEDS_MUTE_ENABLED; + } + t.needs = n; + + if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { + t.hook = track__nop; + } else { + if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + all16BitsStereoNoResample = 0; + resampling = 1; + t.hook = track__genericResample; + } else { + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ + t.hook = track__16BitsMono; + all16BitsStereoNoResample = 0; + } + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){ + t.hook = track__16BitsStereo; + } + } + } + } + + // select the processing hooks + state->hook = process__nop; + if (countActiveTracks) { + if (resampling) { + if (!state->outputTemp) { + state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + if (!state->resampleTemp) { + state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + state->hook = process__genericResampling; + } else { + if (state->outputTemp) { + delete [] state->outputTemp; + state->outputTemp = 0; + } + if (state->resampleTemp) { + delete [] state->resampleTemp; + state->resampleTemp = 0; + } + state->hook = process__genericNoResampling; + if (all16BitsStereoNoResample && !volumeRamp) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } + } + + LOGV("mixer configuration change: %d activeTracks (%08x) " + "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d", + countActiveTracks, state->enabledTracks, + all16BitsStereoNoResample, resampling, volumeRamp); + + state->hook(state, output); + + // Now that the volume ramp has been done, set optimal state and + // track hooks for subsequent mixer process + if (countActiveTracks) { + int allMuted = 1; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + if (!t.doesResample() && t.volumeRL == 0) + { + t.needs |= NEEDS_MUTE_ENABLED; + t.hook = track__nop; + } else { + allMuted = 0; + } + } + if (allMuted) { + state->hook = process__nop; + } else if (!resampling && all16BitsStereoNoResample) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } +} + +static inline +int32_t mulAdd(int16_t in, int16_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlabb %[out], %[in], %[v], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * int32_t(v); +#endif +} + +static inline +int32_t mul(int16_t in, int16_t v) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smulbb %[out], %[in], %[v] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v) + : ); + return out; +#else + return in * int32_t(v); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } else { + asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return a + int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smulbb %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + + +void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ + t->resampler->setSampleRate(t->sampleRate); + + // ramp gain - resample to temp buffer and scale/mix in 2nd step + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); + t->resampler->resample(temp, outFrameCount, t->bufferProvider); + volumeRampStereo(t, out, outFrameCount, temp); + } + + // constant gain + else { + t->resampler->setVolume(t->volume[0], t->volume[1]); + t->resampler->resample(out, outFrameCount, t->bufferProvider); + } +} + +void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ +} + +void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + // ramp volume + do { + *out++ += (vl >> 16) * (*temp++ >> 12); + *out++ += (vr >> 16) * (*temp++ >> 12); + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); +} + +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast<int16_t const *>(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + *out++ += (vl >> 16) * (int32_t) *in++; + *out++ += (vr >> 16) * (int32_t) *in++; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + + // constant gain + else { + const uint32_t vrl = t->volumeRL; + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + out[0] = mulAddRL(1, rl, vrl, out[0]); + out[1] = mulAddRL(0, rl, vrl, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast<int16_t const *>(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + int32_t l = *in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * l; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + // constant gain + else { + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + do { + int16_t l = *in++; + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(l, vr, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) +{ + for (size_t i=0 ; i<c ; i++) { + int32_t l = *sums++; + int32_t r = *sums++; + int32_t nl = l >> 12; + int32_t nr = r >> 12; + l = clamp16(nl); + r = clamp16(nr); + *out++ = (r<<16) | (l & 0xFFFF); + } +} + +// no-op case +void AudioMixer::process__nop(state_t* state, void* output) +{ + // this assumes output 16 bits stereo, no resampling + memset(output, 0, state->frameCount*4); + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + size_t outFrames = state->frameCount; + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + if (!t.buffer.raw) break; + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } +} + +// generic code without resampling +void AudioMixer::process__genericNoResampling(state_t* state, void* output) +{ + int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); + + // acquire each track's buffer + uint32_t enabledTracks = state->enabledTracks; + uint32_t en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + t.buffer.frameCount = state->frameCount; + t.bufferProvider->getNextBuffer(&t.buffer); + t.frameCount = t.buffer.frameCount; + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) + enabledTracks &= ~(1<<i); + } + + // this assumes output 16 bits stereo, no resampling + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + do { + memset(outTemp, 0, sizeof(outTemp)); + + en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + size_t outFrames = BLOCKSIZE; + + while (outFrames) { + size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; + if (inFrames) { + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); + t.frameCount -= inFrames; + outFrames -= inFrames; + } + if (t.frameCount == 0 && outFrames) { + t.bufferProvider->releaseBuffer(&t.buffer); + t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames); + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + if (t.in == NULL) { + enabledTracks &= ~(1<<i); + break; + } + t.frameCount = t.buffer.frameCount; + } + } + } + + ditherAndClamp(out, outTemp, BLOCKSIZE); + out += BLOCKSIZE; + numFrames -= BLOCKSIZE; + } while (numFrames); + + + // release each track's buffer + en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + t.bufferProvider->releaseBuffer(&t.buffer); + } +} + +// generic code with resampling +void AudioMixer::process__genericResampling(state_t* state, void* output) +{ + int32_t* const outTemp = state->outputTemp; + const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; + memset(outTemp, 0, size); + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + + // this is a little goofy, on the resampling case we don't + // acquire/release the buffers because it's done by + // the resampler. + if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + } else { + + size_t outFrames = numFrames; + + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) break; + + (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } + } + + ditherAndClamp(out, outTemp, numFrames); +} + +// one track, 16 bits stereo without resampling is the most common case +void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output) +{ + const int i = 31 - __builtin_clz(state->enabledTracks); + const track_t& t = state->tracks[i]; + + AudioBufferProvider::Buffer& b(t.buffer); + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + + const int16_t vl = t.volume[0]; + const int16_t vr = t.volume[1]; + const uint32_t vrl = t.volumeRL; + while (numFrames) { + b.frameCount = numFrames; + t.bufferProvider->getNextBuffer(&b); + int16_t const *in = b.i16; + + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL || ((unsigned long)in & 3)) { + memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); + LOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: buffer %p track %d, channels %d, needs %08x", + in, i, t.channelCount, t.needs); + return; + } + size_t outFrames = b.frameCount; + + if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { + // volume is boosted, so we might need to clamp even though + // we process only one track. + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } else { + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } + numFrames -= b.frameCount; + t.bufferProvider->releaseBuffer(&b); + } +} + +// 2 tracks is also a common case +void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output) +{ + int i; + uint32_t en = state->enabledTracks; + + i = 31 - __builtin_clz(en); + const track_t& t0 = state->tracks[i]; + AudioBufferProvider::Buffer& b0(t0.buffer); + + en &= ~(1<<i); + i = 31 - __builtin_clz(en); + const track_t& t1 = state->tracks[i]; + AudioBufferProvider::Buffer& b1(t1.buffer); + + int16_t const *in0; + const int16_t vl0 = t0.volume[0]; + const int16_t vr0 = t0.volume[1]; + size_t frameCount0 = 0; + + int16_t const *in1; + const int16_t vl1 = t1.volume[0]; + const int16_t vr1 = t1.volume[1]; + size_t frameCount1 = 0; + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + int16_t const *buff = NULL; + + + while (numFrames) { + + if (frameCount0 == 0) { + b0.frameCount = numFrames; + t0.bufferProvider->getNextBuffer(&b0); + if (b0.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in0 = buff; + b0.frameCount = numFrames; + } else { + in0 = b0.i16; + } + frameCount0 = b0.frameCount; + } + if (frameCount1 == 0) { + b1.frameCount = numFrames; + t1.bufferProvider->getNextBuffer(&b1); + if (b1.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in1 = buff; + b1.frameCount = numFrames; + } else { + in1 = b1.i16; + } + frameCount1 = b1.frameCount; + } + + size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; + + numFrames -= outFrames; + frameCount0 -= outFrames; + frameCount1 -= outFrames; + + do { + int32_t l0 = *in0++; + int32_t r0 = *in0++; + l0 = mul(l0, vl0); + r0 = mul(r0, vr0); + int32_t l = *in1++; + int32_t r = *in1++; + l = mulAdd(l, vl1, l0) >> 12; + r = mulAdd(r, vr1, r0) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + + if (frameCount0 == 0) { + t0.bufferProvider->releaseBuffer(&b0); + } + if (frameCount1 == 0) { + t1.bufferProvider->releaseBuffer(&b1); + } + } + + if (buff != NULL) { + delete [] buff; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h new file mode 100644 index 0000000..15766cd --- /dev/null +++ b/services/audioflinger/AudioMixer.h @@ -0,0 +1,193 @@ +/* //device/include/server/AudioFlinger/AudioMixer.h +** +** 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_MIXER_H +#define ANDROID_AUDIO_MIXER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "AudioBufferProvider.h" +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// ---------------------------------------------------------------------------- + +class AudioMixer +{ +public: + AudioMixer(size_t frameCount, uint32_t sampleRate); + + ~AudioMixer(); + + static const uint32_t MAX_NUM_TRACKS = 32; + static const uint32_t MAX_NUM_CHANNELS = 2; + + static const uint16_t UNITY_GAIN = 0x1000; + + enum { // names + + // track units (32 units) + TRACK0 = 0x1000, + + // enable/disable + MIXING = 0x2000, + + // setParameter targets + TRACK = 0x3000, + RESAMPLE = 0x3001, + RAMP_VOLUME = 0x3002, // ramp to new volume + VOLUME = 0x3003, // don't ramp + + // set Parameter names + // for target TRACK + CHANNEL_COUNT = 0x4000, + FORMAT = 0x4001, + // for TARGET RESAMPLE + SAMPLE_RATE = 0x4100, + // for TARGET VOLUME (8 channels max) + VOLUME0 = 0x4200, + VOLUME1 = 0x4201, + }; + + + int getTrackName(); + void deleteTrackName(int name); + + status_t enable(int name); + status_t disable(int name); + + status_t setActiveTrack(int track); + status_t setParameter(int target, int name, int value); + + status_t setBufferProvider(AudioBufferProvider* bufferProvider); + void process(void* output); + + uint32_t trackNames() const { return mTrackNames; } + + static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + +private: + + enum { + NEEDS_CHANNEL_COUNT__MASK = 0x00000003, + NEEDS_FORMAT__MASK = 0x000000F0, + NEEDS_MUTE__MASK = 0x00000100, + NEEDS_RESAMPLE__MASK = 0x00001000, + }; + + enum { + NEEDS_CHANNEL_1 = 0x00000000, + NEEDS_CHANNEL_2 = 0x00000001, + + NEEDS_FORMAT_16 = 0x00000010, + + NEEDS_MUTE_DISABLED = 0x00000000, + NEEDS_MUTE_ENABLED = 0x00000100, + + NEEDS_RESAMPLE_DISABLED = 0x00000000, + NEEDS_RESAMPLE_ENABLED = 0x00001000, + }; + + static inline int32_t applyVolume(int32_t in, int32_t v) { + return in * v; + } + + + struct state_t; + + typedef void (*mix_t)(state_t* state, void* output); + + static const int BLOCKSIZE = 16; // 4 cache lines + + struct track_t { + uint32_t needs; + + union { + int16_t volume[2]; // [0]3.12 fixed point + int32_t volumeRL; + }; + + int32_t prevVolume[2]; + + int32_t volumeInc[2]; + + uint16_t frameCount; + + uint8_t channelCount : 4; + uint8_t enabled : 1; + uint8_t reserved0 : 3; + uint8_t format; + + AudioBufferProvider* bufferProvider; + mutable AudioBufferProvider::Buffer buffer; + + void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp); + void const* in; // current location in buffer + + AudioResampler* resampler; + uint32_t sampleRate; + + bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); + bool doesResample() const; + void adjustVolumeRamp(); + }; + + // pad to 32-bytes to fill cache line + struct state_t { + uint32_t enabledTracks; + uint32_t needsChanged; + size_t frameCount; + mix_t hook; + int32_t *outputTemp; + int32_t *resampleTemp; + int32_t reserved[2]; + track_t tracks[32]; __attribute__((aligned(32))); + }; + + int mActiveTrack; + uint32_t mTrackNames; + const uint32_t mSampleRate; + + state_t mState __attribute__((aligned(32))); + + void invalidateState(uint32_t mask); + + static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); + static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + + static void process__validate(state_t* state, void* output); + static void process__nop(state_t* state, void* output); + static void process__genericNoResampling(state_t* state, void* output); + static void process__genericResampling(state_t* state, void* output); + static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output); + static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_MIXER_H diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp new file mode 100644 index 0000000..c8b3f48 --- /dev/null +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -0,0 +1,1972 @@ +/* + * Copyright (C) 2009 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 "AudioPolicyManagerBase" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include <hardware_legacy/AudioPolicyManagerBase.h> +#include <media/mediarecorder.h> + +namespace android { + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address) +{ + + LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address); + + // connect/disconnect only 1 device at a time + if (AudioSystem::popCount(device) != 1) return BAD_VALUE; + + if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) { + LOGE("setDeviceConnectionState() invalid address: %s", device_address); + return BAD_VALUE; + } + + // handle output devices + if (AudioSystem::isOutputDevice(device)) { + +#ifndef WITH_A2DP + if (AudioSystem::isA2dpDevice(device)) { + LOGE("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; + } +#endif + + switch (state) + { + // handle output device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: + if (mAvailableOutputDevices & device) { + LOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + LOGV("setDeviceConnectionState() connecting device %x", device); + + // register new device as available + mAvailableOutputDevices |= device; + +#ifdef WITH_A2DP + // handle A2DP device connection + if (AudioSystem::isA2dpDevice(device)) { + status_t status = handleA2dpConnection(device, device_address); + if (status != NO_ERROR) { + mAvailableOutputDevices &= ~device; + return status; + } + } else +#endif + { + if (AudioSystem::isBluetoothScoDevice(device)) { + LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address); + // keep track of SCO device address + mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); +#ifdef WITH_A2DP + if (mA2dpOutput != 0 && + mPhoneState != AudioSystem::MODE_NORMAL) { + mpClientInterface->suspendOutput(mA2dpOutput); + } +#endif + } + } + break; + // handle output device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: { + if (!(mAvailableOutputDevices & device)) { + LOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + + + LOGV("setDeviceConnectionState() disconnecting device %x", device); + // remove device from available output devices + mAvailableOutputDevices &= ~device; + +#ifdef WITH_A2DP + // handle A2DP device disconnection + if (AudioSystem::isA2dpDevice(device)) { + status_t status = handleA2dpDisconnection(device, device_address); + if (status != NO_ERROR) { + mAvailableOutputDevices |= device; + return status; + } + } else +#endif + { + if (AudioSystem::isBluetoothScoDevice(device)) { + mScoDeviceAddress = ""; +#ifdef WITH_A2DP + if (mA2dpOutput != 0 && + mPhoneState != AudioSystem::MODE_NORMAL) { + mpClientInterface->restoreOutput(mA2dpOutput); + } +#endif + } + } + } break; + + default: + LOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + // request routing change if necessary + uint32_t newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP + checkOutputForAllStrategies(newDevice); + // A2DP outputs must be closed after checkOutputForAllStrategies() is executed + if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) { + closeA2dpOutputs(); + } +#endif + updateDeviceForStrategy(); + setOutputDevice(mHardwareOutput, newDevice); + + if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) { + device = AudioSystem::DEVICE_IN_WIRED_HEADSET; + } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO || + device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET || + device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { + device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else { + return NO_ERROR; + } + } + // handle input devices + if (AudioSystem::isInputDevice(device)) { + + switch (state) + { + // handle input device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: { + if (mAvailableInputDevices & device) { + LOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + mAvailableInputDevices |= device; + } + break; + + // handle input device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: { + if (!(mAvailableInputDevices & device)) { + LOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + mAvailableInputDevices &= ~device; + } break; + + default: + LOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput); + uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource); + if (newDevice != inputDesc->mDevice) { + LOGV("setDeviceConnectionState() changing device from %x to %x for input %d", + inputDesc->mDevice, newDevice, activeInput); + inputDesc->mDevice = newDevice; + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)newDevice); + mpClientInterface->setParameters(activeInput, param.toString()); + } + } + + return NO_ERROR; + } + + LOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address) +{ + AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE; + String8 address = String8(device_address); + if (AudioSystem::isOutputDevice(device)) { + if (device & mAvailableOutputDevices) { +#ifdef WITH_A2DP + if (AudioSystem::isA2dpDevice(device) && + address != "" && mA2dpDeviceAddress != address) { + return state; + } +#endif + if (AudioSystem::isBluetoothScoDevice(device) && + address != "" && mScoDeviceAddress != address) { + return state; + } + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } else if (AudioSystem::isInputDevice(device)) { + if (device & mAvailableInputDevices) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } + + return state; +} + +void AudioPolicyManagerBase::setPhoneState(int state) +{ + LOGV("setPhoneState() state %d", state); + uint32_t newDevice = 0; + if (state < 0 || state >= AudioSystem::NUM_MODES) { + LOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + LOGW("setPhoneState() setting same state %d", state); + return; + } + + // if leaving call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + LOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, false, true); + } + } + + // 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 ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) { + LOGV(" Entering call in setPhoneState()"); + // force routing command to audio hardware when starting a call + // even if no device change is needed + force = true; + } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) { + LOGV(" Exiting call in setPhoneState()"); + // force routing command to audio hardware when exiting a call + // even if no device change is needed + force = true; + } + + // check for device and output changes triggered by new phone state + newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP + checkOutputForAllStrategies(newDevice); + // suspend A2DP output if a SCO device is present. + if (mA2dpOutput != 0 && mScoDeviceAddress != "") { + if (oldState == AudioSystem::MODE_NORMAL) { + mpClientInterface->suspendOutput(mA2dpOutput); + } else if (state == AudioSystem::MODE_NORMAL) { + mpClientInterface->restoreOutput(mA2dpOutput); + } + } +#endif + updateDeviceForStrategy(); + + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + + // force routing command to audio hardware when ending call + // even if no device change is needed + if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) { + newDevice = hwOutputDesc->device(); + } + + // when changing from ring tone to in call mode, mute the ringing tone + // immediately and delay the route change to avoid sending the ring tone + // tail into the earpiece or headset. + int delayMs = 0; + if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) { + // delay the device change command by twice the output latency to have some margin + // and be sure that audio buffers not yet affected by the mute are out when + // we actually apply the route change + delayMs = hwOutputDesc->mLatency*2; + setStreamMute(AudioSystem::RING, true, mHardwareOutput); + } + + // change routing is necessary + setOutputDevice(mHardwareOutput, newDevice, force, delayMs); + + // if entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (state == AudioSystem::MODE_IN_CALL) { + LOGV("setPhoneState() in call state management: new state is %d", state); + // unmute the ringing tone after a sufficient delay if it was muted before + // setting output device above + if (oldState == AudioSystem::MODE_RINGTONE) { + setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS); + } + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, true, true); + } + } + + // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE + if (state == AudioSystem::MODE_RINGTONE && + (hwOutputDesc->mRefCount[AudioSystem::MUSIC] || + (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) { + mLimitRingtoneVolume = true; + } else { + mLimitRingtoneVolume = false; + } +} + +void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask) +{ + LOGV("setRingerMode() mode %x, mask %x", mode, mask); + + mRingerMode = mode; +} + +void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + + bool forceVolumeReeval = false; + switch(usage) { + case AudioSystem::FOR_COMMUNICATION: + if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO && + config != AudioSystem::FORCE_NONE) { + LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return; + } + mForceUse[usage] = config; + break; + case AudioSystem::FOR_MEDIA: + if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP && + config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) { + LOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return; + } + mForceUse[usage] = config; + break; + case AudioSystem::FOR_RECORD: + if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY && + config != AudioSystem::FORCE_NONE) { + LOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return; + } + mForceUse[usage] = config; + break; + case AudioSystem::FOR_DOCK: + if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK && + config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) { + LOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + default: + LOGW("setForceUse() invalid usage %d", usage); + break; + } + + // check for device and output changes triggered by new phone state + uint32_t newDevice = getNewDevice(mHardwareOutput, false); +#ifdef WITH_A2DP + checkOutputForAllStrategies(newDevice); +#endif + updateDeviceForStrategy(); + setOutputDevice(mHardwareOutput, newDevice); + if (forceVolumeReeval) { + applyStreamVolumes(mHardwareOutput, newDevice); + } +} + +AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value) +{ + LOGV("setSystemProperty() property %s, value %s", property, value); + if (strcmp(property, "ro.camera.sound.forced") == 0) { + if (atoi(value)) { + LOGV("ENFORCED_AUDIBLE cannot be muted"); + mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false; + } else { + LOGV("ENFORCED_AUDIBLE can be muted"); + mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true; + } + } +} + +audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags) +{ + audio_io_handle_t output = 0; + uint32_t latency = 0; + routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + uint32_t device = getDeviceForStrategy(strategy); + LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags); + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + LOGV("getOutput() opening test output"); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = mTestDevice; + outputDesc->mSamplingRate = mTestSamplingRate; + outputDesc->mFormat = mTestFormat; + outputDesc->mChannels = mTestChannels; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + if (mTestOutputs[mCurOutput]) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"),mCurOutput); + mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); + addOutput(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + + // open a direct output if required by specified parameters + if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) { + + LOGV("getOutput() opening direct output device %x", device); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = device; + outputDesc->mSamplingRate = samplingRate; + outputDesc->mFormat = format; + outputDesc->mChannels = channels; + outputDesc->mLatency = 0; + outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT); + outputDesc->mRefCount[stream] = 0; + output = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + + // only accept an output with the requeted parameters + if (output == 0 || + (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) || + (format != 0 && format != outputDesc->mFormat) || + (channels != 0 && channels != outputDesc->mChannels)) { + LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d", + samplingRate, format, channels); + if (output != 0) { + mpClientInterface->closeOutput(output); + } + delete outputDesc; + return 0; + } + addOutput(output, outputDesc); + return output; + } + + if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && + channels != AudioSystem::CHANNEL_OUT_STEREO) { + return 0; + } + // open a non direct output + + // get which output is suitable for the specified stream. The actual routing change will happen + // when startOutput() will be called + uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP; + if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) { +#ifdef WITH_A2DP + if (a2dpUsedForSonification() && a2dpDevice != 0) { + // if playing on 2 devices among which one is A2DP, use duplicated output + LOGV("getOutput() using duplicated output"); + LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device); + output = mDuplicatedOutput; + } else +#endif + { + // if playing on 2 devices among which none is A2DP, use hardware output + output = mHardwareOutput; + } + LOGV("getOutput() using output %d for 2 devices %x", output, device); + } else { +#ifdef WITH_A2DP + if (a2dpDevice != 0) { + // if playing on A2DP device, use a2dp output + LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device); + output = mA2dpOutput; + } else +#endif + { + // if playing on not A2DP device, use hardware output + output = mHardwareOutput; + } + } + + + LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x", + stream, samplingRate, format, channels, flags); + + return output; +} + +status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + LOGV("startOutput() output %d, stream %d", output, stream); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("startOutput() unknow output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + +#ifdef WITH_A2DP + if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) { + setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); + } +#endif + + // incremenent usage count for this stream on the requested output: + // NOTE that the usage count is the same for duplicated output and hardware output which is + // necassary for a correct control of hardware output routing by startOutput() and stopOutput() + outputDesc->changeRefCount(stream, 1); + + setOutputDevice(output, getNewDevice(output)); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, true, false); + } + + // apply volume rules for current stream and device if necessary + checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device()); + + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + LOGV("stopOutput() output %d, stream %d", output, stream); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("stopOutput() unknow output %d", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, false, false); + } + + if (outputDesc->mRefCount[stream] > 0) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + // store time at which the last music track was stopped - see computeVolume() + if (stream == AudioSystem::MUSIC) { + mMusicStopTime = systemTime(); + } + + setOutputDevice(output, getNewDevice(output)); + +#ifdef WITH_A2DP + if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) { + setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2); + } +#endif + if (output != mHardwareOutput) { + setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true); + } + return NO_ERROR; + } else { + LOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output) +{ + LOGV("releaseOutput() %d", output); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("releaseOutput() releasing unknown output %d", output); + return; + } + +#ifdef AUDIO_POLICY_TEST + int testIndex = testOutputIndex(output); + if (testIndex != 0) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + if (outputDesc->refCount() == 0) { + mpClientInterface->closeOutput(output); + delete mOutputs.valueAt(index); + mOutputs.removeItem(output); + mTestOutputs[testIndex] = 0; + } + return; + } +#endif //AUDIO_POLICY_TEST + + if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) { + mpClientInterface->closeOutput(output); + delete mOutputs.valueAt(index); + mOutputs.removeItem(output); + } +} + +audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics) +{ + audio_io_handle_t input = 0; + uint32_t device = getDeviceForInputSource(inputSource); + + LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics); + + if (device == 0) { + return 0; + } + + // adapt channel selection to input source + switch(inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK); + break; + default: + break; + } + + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(); + + inputDesc->mInputSource = inputSource; + inputDesc->mDevice = device; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannels = channels; + inputDesc->mAcoustics = acoustics; + inputDesc->mRefCount = 0; + input = mpClientInterface->openInput(&inputDesc->mDevice, + &inputDesc->mSamplingRate, + &inputDesc->mFormat, + &inputDesc->mChannels, + inputDesc->mAcoustics); + + // only accept input with the exact requested set of parameters + if (input == 0 || + (samplingRate != inputDesc->mSamplingRate) || + (format != inputDesc->mFormat) || + (channels != inputDesc->mChannels)) { + LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d", + samplingRate, format, channels); + if (input != 0) { + mpClientInterface->closeInput(input); + } + delete inputDesc; + return 0; + } + mInputs.add(input, inputDesc); + return input; +} + +status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input) +{ + LOGV("startInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("startInput() unknow input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +#ifdef AUDIO_POLICY_TEST + if (mTestInput == 0) +#endif //AUDIO_POLICY_TEST + { + // refuse 2 active AudioRecord clients at the same time + if (getActiveInput() != 0) { + LOGW("startInput() input %d failed: other input already started", input); + return INVALID_OPERATION; + } + } + + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice); + + // use Voice Recognition mode or not for this input based on input source + int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0; + param.addInt(String8("vr_mode"), vr_enabled); + LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled); + + mpClientInterface->setParameters(input, param.toString()); + + inputDesc->mRefCount = 1; + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input) +{ + LOGV("stopInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("stopInput() unknow input %d", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + + if (inputDesc->mRefCount == 0) { + LOGW("stopInput() input %d already stopped", input); + return INVALID_OPERATION; + } else { + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), 0); + mpClientInterface->setParameters(input, param.toString()); + inputDesc->mRefCount = 0; + return NO_ERROR; + } +} + +void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input) +{ + LOGV("releaseInput() %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("releaseInput() releasing unknown input %d", input); + return; + } + mpClientInterface->closeInput(input); + delete mInputs.valueAt(index); + mInputs.removeItem(input); + LOGV("releaseInput() exit"); +} + +void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMin >= indexMax) { + LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); + return; + } + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + + LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index); + mStreams[stream].mIndexCur = index; + + // compute and apply stream volume on all outputs according to connected device + status_t status = NO_ERROR; + for (size_t i = 0; i < mOutputs.size(); i++) { + status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device()); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + return status; +} + +status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) +{ + if (index == 0) { + return BAD_VALUE; + } + LOGV("getStreamVolumeIndex() stream %d", stream); + *index = mStreams[stream].mIndexCur; + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); + result.append(buffer); + snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput); + result.append(buffer); +#ifdef WITH_A2DP + snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput); + result.append(buffer); + snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput); + result.append(buffer); + snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string()); + result.append(buffer); +#endif + snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string()); + result.append(buffer); + snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices); + result.append(buffer); + snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices); + result.append(buffer); + snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); + result.append(buffer); + snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]); + result.append(buffer); + write(fd, result.string(), result.size()); + + 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 Index Min Index Max Index Cur Can be muted\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + snprintf(buffer, SIZE, " %02d", i); + mStreams[i].dump(buffer + 3, SIZE); + write(fd, buffer, strlen(buffer)); + } + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerBase +// ---------------------------------------------------------------------------- + +AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface) + : +#ifdef AUDIO_POLICY_TEST + Thread(false), +#endif //AUDIO_POLICY_TEST + mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false) +{ + mpClientInterface = clientInterface; + + for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) { + mForceUse[i] = AudioSystem::FORCE_NONE; + } + + // devices available by default are speaker, ear piece and microphone + mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE | + AudioSystem::DEVICE_OUT_SPEAKER; + mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; + +#ifdef WITH_A2DP + mA2dpOutput = 0; + mDuplicatedOutput = 0; + mA2dpDeviceAddress = String8(""); +#endif + mScoDeviceAddress = String8(""); + + // open hardware output + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER; + mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + + if (mHardwareOutput == 0) { + LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels); + } else { + addOutput(mHardwareOutput, outputDesc); + setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true); + } + + updateDeviceForStrategy(); +#ifdef AUDIO_POLICY_TEST + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString()); + + mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER; + mTestSamplingRate = 44100; + mTestFormat = AudioSystem::PCM_16_BIT; + mTestChannels = AudioSystem::CHANNEL_OUT_STEREO; + mTestLatencyMs = 0; + mCurOutput = 0; + mDirectOutput = false; + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + mTestOutputs[i] = 0; + } + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "AudioPolicyManagerTest"); + run(buffer, ANDROID_PRIORITY_AUDIO); +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManagerBase::~AudioPolicyManagerBase() +{ +#ifdef AUDIO_POLICY_TEST + exit(); +#endif //AUDIO_POLICY_TEST + for (size_t i = 0; i < mOutputs.size(); i++) { + mpClientInterface->closeOutput(mOutputs.keyAt(i)); + delete mOutputs.valueAt(i); + } + mOutputs.clear(); + for (size_t i = 0; i < mInputs.size(); i++) { + mpClientInterface->closeInput(mInputs.keyAt(i)); + delete mInputs.valueAt(i); + } + mInputs.clear(); +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManagerBase::threadLoop() +{ + LOGV("entering threadLoop()"); + while (!exitPending()) + { + String8 command; + int valueInt; + String8 value; + + Mutex::Autolock _l(mLock); + mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + + command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); + AudioParameter param = AudioParameter(command); + + if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR && + valueInt != 0) { + LOGV("Test command %s received", command.string()); + String8 target; + if (param.get(String8("target"), target) != NO_ERROR) { + target = "Manager"; + } + if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_output")); + mCurOutput = valueInt; + } + if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_direct")); + if (value == "false") { + mDirectOutput = false; + } else if (value == "true") { + mDirectOutput = true; + } + } + if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_input")); + mTestInput = valueInt; + } + + if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_format")); + int format = AudioSystem::INVALID_FORMAT; + if (value == "PCM 16 bits") { + format = AudioSystem::PCM_16_BIT; + } else if (value == "PCM 8 bits") { + format = AudioSystem::PCM_8_BIT; + } else if (value == "Compressed MP3") { + format = AudioSystem::MP3; + } + if (format != AudioSystem::INVALID_FORMAT) { + if (target == "Manager") { + mTestFormat = format; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("format"), format); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_channels")); + int channels = 0; + + if (value == "Channels Stereo") { + channels = AudioSystem::CHANNEL_OUT_STEREO; + } else if (value == "Channels Mono") { + channels = AudioSystem::CHANNEL_OUT_MONO; + } + if (channels != 0) { + if (target == "Manager") { + mTestChannels = channels; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("channels"), channels); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_sampleRate")); + if (valueInt >= 0 && valueInt <= 96000) { + int samplingRate = valueInt; + if (target == "Manager") { + mTestSamplingRate = samplingRate; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("sampling_rate"), samplingRate); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + + if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_reopen")); + + mpClientInterface->closeOutput(mHardwareOutput); + delete mOutputs.valueFor(mHardwareOutput); + mOutputs.removeItem(mHardwareOutput); + + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER; + mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + if (mHardwareOutput == 0) { + LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels); + } else { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString()); + addOutput(mHardwareOutput, outputDesc); + } + } + + + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManagerBase::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output) +{ + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + if (output == mTestOutputs[i]) return i; + } + return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc) +{ + outputDesc->mId = id; + mOutputs.add(id, outputDesc); +} + + +#ifdef WITH_A2DP +status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device, + const char *device_address) +{ + // when an A2DP device is connected, open an A2DP and a duplicated output + LOGV("opening A2DP output for device %s", device_address); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = device; + mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + if (mA2dpOutput) { + // add A2DP output descriptor + addOutput(mA2dpOutput, outputDesc); + // set initial stream volume for A2DP device + applyStreamVolumes(mA2dpOutput, device); + if (a2dpUsedForSonification()) { + mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput); + } + if (mDuplicatedOutput != 0 || + !a2dpUsedForSonification()) { + // If both A2DP and duplicated outputs are open, send device address to A2DP hardware + // interface + AudioParameter param; + param.add(String8("a2dp_sink_address"), String8(device_address)); + mpClientInterface->setParameters(mA2dpOutput, param.toString()); + mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN); + + if (a2dpUsedForSonification()) { + // add duplicated output descriptor + AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor(); + dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput); + dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput); + dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate; + dupOutputDesc->mFormat = outputDesc->mFormat; + dupOutputDesc->mChannels = outputDesc->mChannels; + dupOutputDesc->mLatency = outputDesc->mLatency; + addOutput(mDuplicatedOutput, dupOutputDesc); + applyStreamVolumes(mDuplicatedOutput, device); + } + } else { + LOGW("getOutput() could not open duplicated output for %d and %d", + mHardwareOutput, mA2dpOutput); + mpClientInterface->closeOutput(mA2dpOutput); + mOutputs.removeItem(mA2dpOutput); + mA2dpOutput = 0; + delete outputDesc; + return NO_INIT; + } + } else { + LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device); + delete outputDesc; + return NO_INIT; + } + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + + if (mScoDeviceAddress != "") { + // It is normal to suspend twice if we are both in call, + // and have the hardware audio output routed to BT SCO + if (mPhoneState != AudioSystem::MODE_NORMAL) { + mpClientInterface->suspendOutput(mA2dpOutput); + } + if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) { + mpClientInterface->suspendOutput(mA2dpOutput); + } + } + + if (!a2dpUsedForSonification()) { + // mute music on A2DP output if a notification or ringtone is playing + uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION); + for (uint32_t i = 0; i < refCount; i++) { + setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput); + } + } + return NO_ERROR; +} + +status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device, + const char *device_address) +{ + if (mA2dpOutput == 0) { + LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!"); + return INVALID_OPERATION; + } + + if (mA2dpDeviceAddress != device_address) { + LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address); + return INVALID_OPERATION; + } + + // mute media strategy to avoid outputting sound on hardware output while music stream + // is switched from A2DP output and before music is paused by music application + setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput); + setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS); + + if (!a2dpUsedForSonification()) { + // unmute music on A2DP output if a notification or ringtone is playing + uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION); + for (uint32_t i = 0; i < refCount; i++) { + setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput); + } + } + mA2dpDeviceAddress = ""; + return NO_ERROR; +} + +void AudioPolicyManagerBase::closeA2dpOutputs() +{ + LOGV("setDeviceConnectionState() closing A2DP and duplicated output!"); + + if (mDuplicatedOutput != 0) { + mpClientInterface->closeOutput(mDuplicatedOutput); + delete mOutputs.valueFor(mDuplicatedOutput); + mOutputs.removeItem(mDuplicatedOutput); + mDuplicatedOutput = 0; + } + if (mA2dpOutput != 0) { + AudioParameter param; + param.add(String8("closing"), String8("true")); + mpClientInterface->setParameters(mA2dpOutput, param.toString()); + mpClientInterface->closeOutput(mA2dpOutput); + delete mOutputs.valueFor(mA2dpOutput); + mOutputs.removeItem(mA2dpOutput); + mA2dpOutput = 0; + } +} + +void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, uint32_t &newDevice) +{ + uint32_t prevDevice = getDeviceForStrategy(strategy); + uint32_t curDevice = getDeviceForStrategy(strategy, false); + bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER)); + bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER)); + AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput); + AudioOutputDescriptor *a2dpOutputDesc; + + if (a2dpWasUsed && !a2dpIsUsed) { + bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2); + + if (dupUsed) { + LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy); + a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + } else { + LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy); + a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput); + } + + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput); + int refCount = a2dpOutputDesc->mRefCount[i]; + // in the case of duplicated output, the ref count is first incremented + // and then decremented on hardware output tus keeping its value + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); + a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + } + // do not change newDevice if it was already set before this call by a previous call to + // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority + if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) { + newDevice = getDeviceForStrategy(strategy, false); + } + } + if (a2dpIsUsed && !a2dpWasUsed) { + bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2); + audio_io_handle_t a2dpOutput; + + if (dupUsed) { + LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy); + a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput); + a2dpOutput = mDuplicatedOutput; + } else { + LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy); + a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput); + a2dpOutput = mA2dpOutput; + } + + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput); + int refCount = hwOutputDesc->mRefCount[i]; + // in the case of duplicated output, the ref count is first incremented + // and then decremented on hardware output tus keeping its value + a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount); + hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount); + } + } + } +} + +void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice) +{ + // Check strategies in order of priority so that once newDevice is set + // for a given strategy it is not modified by subsequent calls to + // checkOutputForStrategy() + checkOutputForStrategy(STRATEGY_PHONE, newDevice); + checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice); + checkOutputForStrategy(STRATEGY_MEDIA, newDevice); + checkOutputForStrategy(STRATEGY_DTMF, newDevice); +} + +#endif + +uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache) +{ + uint32_t device = 0; + + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + // check the following by order of priority to request a routing change if necessary: + // 1: we are in call or the strategy phone is active on the hardware output: + // use device for strategy phone + // 2: the strategy sonification is active on the hardware output: + // use device for strategy sonification + // 3: the strategy media is active on the hardware output: + // use device for strategy media + // 4: the strategy DTMF is active on the hardware output: + // use device for strategy DTMF + if (mPhoneState == AudioSystem::MODE_IN_CALL || + outputDesc->isUsedByStrategy(STRATEGY_PHONE)) { + device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); + } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); + } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) { + device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); + } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) { + device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + } + + LOGV("getNewDevice() selected device %x", device); + return device; +} + +AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(AudioSystem::stream_type stream) +{ + // stream to strategy mapping + switch (stream) { + case AudioSystem::VOICE_CALL: + case AudioSystem::BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AudioSystem::RING: + case AudioSystem::NOTIFICATION: + case AudioSystem::ALARM: + case AudioSystem::ENFORCED_AUDIBLE: + return STRATEGY_SONIFICATION; + case AudioSystem::DTMF: + return STRATEGY_DTMF; + default: + LOGE("unknown stream type"); + case AudioSystem::SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AudioSystem::TTS: + case AudioSystem::MUSIC: + return STRATEGY_MEDIA; + } +} + +uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache) +{ + uint32_t device = 0; + + if (fromCache) { + LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]); + return mDeviceForStrategy[strategy]; + } + + switch (strategy) { + case STRATEGY_DTMF: + if (mPhoneState != AudioSystem::MODE_IN_CALL) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA, false); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { + case AudioSystem::FORCE_BT_SCO: + if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::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 + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; + if (device) break; +#ifdef WITH_A2DP + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (mPhoneState != AudioSystem::MODE_IN_CALL) { + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } +#endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE; + if (device == 0) { + LOGE("getDeviceForStrategy() earpiece device not found"); + } + break; + + case AudioSystem::FORCE_SPEAKER: + if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } +#ifdef WITH_A2DP + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (mPhoneState != AudioSystem::MODE_IN_CALL) { + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } +#endif + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; + if (device == 0) { + LOGE("getDeviceForStrategy() speaker device not found"); + } + break; + } + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + device = getDeviceForStrategy(STRATEGY_PHONE, false); + break; + } + device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; + if (device == 0) { + LOGE("getDeviceForStrategy() speaker device not found"); + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + case STRATEGY_MEDIA: { + uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL; + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET; + } +#ifdef WITH_A2DP + if (mA2dpOutput != 0) { + if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) { + break; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } +#endif + if (device2 == 0) { + device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER; + } + + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise + device |= device2; + if (device == 0) { + LOGE("getDeviceForStrategy() speaker device not found"); + } + } break; + + default: + LOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + +void AudioPolicyManagerBase::updateDeviceForStrategy() +{ + for (int i = 0; i < NUM_STRATEGIES; i++) { + mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false); + } +} + +void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs) +{ + LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs); + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + + + if (outputDesc->isDuplicated()) { + setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs); + setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs); + return; + } +#ifdef WITH_A2DP + // filter devices according to output selected + if (output == mA2dpOutput) { + device &= AudioSystem::DEVICE_OUT_ALL_A2DP; + } else { + device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP; + } +#endif + + uint32_t prevDevice = (uint32_t)outputDesc->device(); + // Do not change the routing if: + // - the requestede device is 0 + // - the requested device is the same as current device and force is not specified. + // Doing this check here allows the caller to call setOutputDevice() without conditions + if ((device == 0 || device == prevDevice) && !force) { + LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output); + return; + } + + outputDesc->mDevice = device; + // mute media streams if both speaker and headset are selected + if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) { + setStrategyMute(STRATEGY_MEDIA, true, output); + // wait for the PCM output buffers to empty before proceeding with the rest of the command + usleep(outputDesc->mLatency*2*1000); + } +#ifdef WITH_A2DP + // suspend A2DP output if SCO device is selected + if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) { + if (mA2dpOutput != 0) { + mpClientInterface->suspendOutput(mA2dpOutput); + } + } +#endif + // do the routing + AudioParameter param = AudioParameter(); + param.addInt(String8(AudioParameter::keyRouting), (int)device); + mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs); + // update stream volumes according to new device + applyStreamVolumes(output, device, delayMs); + +#ifdef WITH_A2DP + // if disconnecting SCO device, restore A2DP output + if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) { + if (mA2dpOutput != 0) { + LOGV("restore A2DP output"); + mpClientInterface->restoreOutput(mA2dpOutput); + } + } +#endif + // if changing from a combined headset + speaker route, unmute media streams + if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) { + setStrategyMute(STRATEGY_MEDIA, false, output, delayMs); + } +} + +uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource) +{ + uint32_t device; + + switch(inputSource) { + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + case AUDIO_SOURCE_VOICE_RECOGNITION: + if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO && + mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) { + device = AudioSystem::DEVICE_IN_WIRED_HEADSET; + } else { + device = AudioSystem::DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (hasBackMicrophone()) { + device = AudioSystem::DEVICE_IN_BACK_MIC; + } else { + device = AudioSystem::DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_UPLINK: + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + device = AudioSystem::DEVICE_IN_VOICE_CALL; + break; + default: + LOGW("getInput() invalid input source %d", inputSource); + device = 0; + break; + } + LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +audio_io_handle_t AudioPolicyManagerBase::getActiveInput() +{ + for (size_t i = 0; i < mInputs.size(); i++) { + if (mInputs.valueAt(i)->mRefCount > 0) { + return mInputs.keyAt(i); + } + } + return 0; +} + +float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device) +{ + float volume = 1.0; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + StreamDescriptor &streamDesc = mStreams[stream]; + + if (device == 0) { + device = outputDesc->device(); + } + + int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); + volume = AudioSystem::linearToLog(volInt); + + // if a headset is connected, apply the following rules to ring tones and notifications + // to avoid sound level bursts in user's ears: + // - always attenuate ring tones and notifications volume by 6dB + // - if music is playing, always limit the volume to current music volume, + // with a minimum threshold at -36dB so that notification is always perceived. + if ((device & + (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP | + AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AudioSystem::DEVICE_OUT_WIRED_HEADSET | + AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && + (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) && + streamDesc.mCanBeMuted) { + 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 + // just stopped + if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) { + float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device); + float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN; + if (volume > minVol) { + volume = minVol; + LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); + } + } + } + + return volume; +} + +status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force) +{ + + // do not change actual stream volume if the stream is muted + if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { + LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]); + return NO_ERROR; + } + + // do not change in call volume if bluetooth is connected and vice versa + if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || + (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) { + LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + float volume = computeVolume(stream, index, output, device); + // do not set volume if the float value did not change + if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) { + mOutputs.valueFor(output)->mCurVolume[stream] = volume; + LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::DTMF || + stream == AudioSystem::BLUETOOTH_SCO) { + float voiceVolume = -1.0; + // offset value to reflect actual hardware volume that never reaches 0 + // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java) + volume = 0.01 + 0.99 * volume; + if (stream == AudioSystem::VOICE_CALL) { + voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + } else if (stream == AudioSystem::BLUETOOTH_SCO) { + voiceVolume = 1.0; + } + if (voiceVolume >= 0 && output == mHardwareOutput) { + mpClientInterface->setVoiceVolume(voiceVolume, delayMs); + } + } + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs); + } + + return NO_ERROR; +} + +void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs) +{ + LOGV("applyStreamVolumes() for output %d and device %x", output, device); + + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs); + } +} + +void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs) +{ + LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + if (getStrategy((AudioSystem::stream_type)stream) == strategy) { + setStreamMute(stream, on, output, delayMs); + } + } +} + +void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs) +{ + StreamDescriptor &streamDesc = mStreams[stream]; + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); + + LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]); + + if (on) { + if (outputDesc->mMuteCount[stream] == 0) { + if (streamDesc.mCanBeMuted) { + checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs); + } + } + // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored + outputDesc->mMuteCount[stream]++; + } else { + if (outputDesc->mMuteCount[stream] == 0) { + LOGW("setStreamMute() unmuting non muted stream!"); + return; + } + if (--outputDesc->mMuteCount[stream] == 0) { + checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs); + } + } +} + +void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange) +{ + // if the stream pertains to sonification strategy and we are in call we must + // mute the stream if it is low visibility. If it is high visibility, we must play a tone + // in the device used for phone strategy and play the tone if the selected device does not + // interfere with the device used for phone strategy + // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as + // many times as there are active tracks on the output + + if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput); + LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", + stream, starting, outputDesc->mDevice, stateChange); + if (outputDesc->mRefCount[stream]) { + int muteCount = 1; + if (stateChange) { + muteCount = outputDesc->mRefCount[stream]; + } + if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { + LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mHardwareOutput); + } + } else { + LOGV("handleIncallSonification() high visibility"); + if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) { + LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mHardwareOutput); + } + } + if (starting) { + mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + +bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags, + uint32_t device) +{ + return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format !=0 && !AudioSystem::isLinearPCM(format))); +} + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor() + : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0), + mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + mRefCount[i] = 0; + mCurVolume[i] = -1.0; + mMuteCount[i] = 0; + } +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device() +{ + uint32_t device = 0; + if (isDuplicated()) { + device = mOutput1->mDevice | mOutput2->mDevice; + } else { + device = mDevice; + } + return device; +} + +void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type 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) { + LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount() +{ + uint32_t refcount = 0; + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + refcount += mRefCount[i]; + } + return refcount; +} + +uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy) +{ + uint32_t refCount = 0; + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + if (getStrategy((AudioSystem::stream_type)i) == strategy) { + refCount += mRefCount[i]; + } + } + return refCount; +} + + +status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + 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", mChannels); + 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 < AudioSystem::NUM_STREAM_TYPES; 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 + +AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor() + : mSamplingRate(0), mFormat(0), mChannels(0), + mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0) +{ +} + +status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + 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", mChannels); + result.append(buffer); + snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- StreamDescriptor class implementation + +void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %02d %02d %02d %d\n", + mIndexMin, + mIndexMax, + mIndexCur, + mCanBeMuted); +} + + +}; // namespace android diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp new file mode 100644 index 0000000..bb3905c --- /dev/null +++ b/services/audioflinger/AudioPolicyService.cpp @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2009 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 "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include <sys/time.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <cutils/properties.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> +#include "AudioPolicyService.h" +#include <hardware_legacy/AudioPolicyManagerBase.h> +#include <cutils/properties.h> +#include <dlfcn.h> +#include <hardware_legacy/power.h> + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +namespace android { + + +static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n"; +static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n"; + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 20000; + +static bool checkPermission() { +#ifndef HAVE_ANDROID_OS + return true; +#endif + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +} + +// ---------------------------------------------------------------------------- + +AudioPolicyService::AudioPolicyService() + : BnAudioPolicyService() , mpPolicyManager(NULL) +{ + char value[PROPERTY_VALUE_MAX]; + + // start tone playback thread + mTonePlaybackThread = new AudioCommandThread(String8("")); + // start audio commands thread + mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread")); + +#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST) + mpPolicyManager = new AudioPolicyManagerBase(this); + LOGV("build for GENERIC_AUDIO - using generic audio policy"); +#else + // if running in emulation - use the emulator driver + if (property_get("ro.kernel.qemu", value, 0)) { + LOGV("Running in emulation - using generic audio policy"); + mpPolicyManager = new AudioPolicyManagerBase(this); + } + else { + LOGV("Using hardware specific audio policy"); + mpPolicyManager = createAudioPolicyManager(this); + } +#endif + + // load properties + property_get("ro.camera.sound.forced", value, "0"); + mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value); +} + +AudioPolicyService::~AudioPolicyService() +{ + mTonePlaybackThread->exit(); + mTonePlaybackThread.clear(); + mAudioCommandThread->exit(); + mAudioCommandThread.clear(); + + if (mpPolicyManager) { + delete mpPolicyManager; + } +} + + +status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) { + return BAD_VALUE; + } + if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + LOGV("setDeviceConnectionState() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->setDeviceConnectionState(device, state, device_address); +} + +AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address) +{ + if (mpPolicyManager == NULL) { + return AudioSystem::DEVICE_STATE_UNAVAILABLE; + } + if (!checkPermission()) { + return AudioSystem::DEVICE_STATE_UNAVAILABLE; + } + return mpPolicyManager->getDeviceConnectionState(device, device_address); +} + +status_t AudioPolicyService::setPhoneState(int state) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (state < 0 || state >= AudioSystem::NUM_MODES) { + return BAD_VALUE; + } + + LOGV("setPhoneState() tid %d", gettid()); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mpPolicyManager->setPhoneState(state); + return NO_ERROR; +} + +status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + + mpPolicyManager->setRingerMode(mode, mask); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) { + return BAD_VALUE; + } + if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) { + return BAD_VALUE; + } + LOGV("setForceUse() tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpPolicyManager->setForceUse(usage, config); + return NO_ERROR; +} + +AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage) +{ + if (mpPolicyManager == NULL) { + return AudioSystem::FORCE_NONE; + } + if (!checkPermission()) { + return AudioSystem::FORCE_NONE; + } + if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) { + return AudioSystem::FORCE_NONE; + } + return mpPolicyManager->getForceUse(usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags) +{ + if (mpPolicyManager == NULL) { + return 0; + } + LOGV("getOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + LOGV("startOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->startOutput(output, stream); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + LOGV("stopOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->stopOutput(output, stream); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mpPolicyManager == NULL) { + return; + } + LOGV("releaseOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpPolicyManager->releaseOutput(output); +} + +audio_io_handle_t AudioPolicyService::getInput(int inputSource, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics) +{ + if (mpPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics); +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->startInput(input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->stopInput(input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mpPolicyManager->releaseInput(input); +} + +status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + mpPolicyManager->initStreamVolume(stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + return mpPolicyManager->setStreamVolumeIndex(stream, index); +} + +status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + return mpPolicyManager->getStreamVolumeIndex(stream, index); +} + +void AudioPolicyService::binderDied(const wp<IBinder>& who) { + LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); +} + +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 AudioPolicyService::dumpInternals(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpPolicyManager); + result.append(buffer); + snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); + result.append(buffer); + snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get()); + result.append(buffer); + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::dump(int fd, const Vector<String16>& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd); + } else { + bool locked = tryLock(mLock); + if (!locked) { + String8 result(kDeadlockedString); + write(fd, result.string(), result.size()); + } + + dumpInternals(fd); + if (mAudioCommandThread != NULL) { + mAudioCommandThread->dump(fd); + } + if (mTonePlaybackThread != NULL) { + mTonePlaybackThread->dump(fd); + } + + if (mpPolicyManager) { + mpPolicyManager->dump(fd); + } + + if (locked) mLock.unlock(); + } + return NO_ERROR; +} + +status_t AudioPolicyService::dumpPermissionDenial(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioPolicyService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioPolicyService::onTransact(code, data, reply, flags); +} + + +// ---------------------------------------------------------------------------- +void AudioPolicyService::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_policy"), new AudioPolicyService()); +} + + +// ---------------------------------------------------------------------------- +// AudioPolicyClientInterface implementation +// ---------------------------------------------------------------------------- + + +audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + AudioSystem::output_flags flags) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openOutput() could not get AudioFlinger"); + return 0; + } + + return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags); +} + +audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openDuplicateOutput() could not get AudioFlinger"); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +status_t AudioPolicyService::closeOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->closeOutput(output); +} + + +status_t AudioPolicyService::suspendOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("suspendOutput() could not get AudioFlinger"); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +status_t AudioPolicyService::restoreOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("restoreOutput() could not get AudioFlinger"); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openInput() could not get AudioFlinger"); + return 0; + } + + return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics); +} + +status_t AudioPolicyService::closeInput(audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->closeInput(input); +} + +status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs) +{ + return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs); +} + +status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->setStreamOutput(stream, output); +} + + +void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs) +{ + mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs); +} + +String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys) +{ + String8 result = AudioSystem::getParameters(ioHandle, keys); + return result; +} + +status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream) +{ + mTonePlaybackThread->startToneCommand(tone, stream); + return NO_ERROR; +} + +status_t AudioPolicyService::stopTone() +{ + mTonePlaybackThread->stopToneCommand(); + return NO_ERROR; +} + +status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs) +{ + return mAudioCommandThread->voiceVolumeCommand(volume, delayMs); +} + +// ----------- AudioPolicyService::AudioCommandThread implementation ---------- + +AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name) + : Thread(false), mName(name) +{ + mpToneGenerator = NULL; +} + + +AudioPolicyService::AudioCommandThread::~AudioCommandThread() +{ + if (mName != "" && !mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + mAudioCommands.clear(); + if (mpToneGenerator != NULL) delete mpToneGenerator; +} + +void AudioPolicyService::AudioCommandThread::onFirstRef() +{ + if (mName != "") { + run(mName.string(), ANDROID_PRIORITY_AUDIO); + } else { + run("AudioCommandThread", ANDROID_PRIORITY_AUDIO); + } +} + +bool AudioPolicyService::AudioCommandThread::threadLoop() +{ + nsecs_t waitTime = INT64_MAX; + + mLock.lock(); + while (!exitPending()) + { + while(!mAudioCommands.isEmpty()) { + nsecs_t curTime = systemTime(); + // commands are sorted by increasing time stamp: execute them from index 0 and up + if (mAudioCommands[0]->mTime <= curTime) { + AudioCommand *command = mAudioCommands[0]; + mAudioCommands.removeAt(0); + mLastCommand = *command; + + switch (command->mCommand) { + case START_TONE: { + mLock.unlock(); + ToneData *data = (ToneData *)command->mParam; + LOGV("AudioCommandThread() processing start tone %d on stream %d", + data->mType, data->mStream); + if (mpToneGenerator != NULL) + delete mpToneGenerator; + mpToneGenerator = new ToneGenerator(data->mStream, 1.0); + mpToneGenerator->startTone(data->mType); + delete data; + mLock.lock(); + }break; + case STOP_TONE: { + mLock.unlock(); + LOGV("AudioCommandThread() processing stop tone"); + if (mpToneGenerator != NULL) { + mpToneGenerator->stopTone(); + delete mpToneGenerator; + mpToneGenerator = NULL; + } + mLock.lock(); + }break; + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam; + LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %d", data->mStream, data->mVolume, data->mIO); + command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO); + if (command->mWaitStatus) { + command->mCond.signal(); + mWaitWorkCV.wait(mLock); + } + delete data; + }break; + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam; + LOGV("AudioCommandThread() processing set parameters string %s, io %d", data->mKeyValuePairs.string(), data->mIO); + command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); + if (command->mWaitStatus) { + command->mCond.signal(); + mWaitWorkCV.wait(mLock); + } + delete data; + }break; + case SET_VOICE_VOLUME: { + VoiceVolumeData *data = (VoiceVolumeData *)command->mParam; + LOGV("AudioCommandThread() processing set voice volume volume %f", data->mVolume); + command->mStatus = AudioSystem::setVoiceVolume(data->mVolume); + if (command->mWaitStatus) { + command->mCond.signal(); + mWaitWorkCV.wait(mLock); + } + delete data; + }break; + default: + LOGW("AudioCommandThread() unknown command %d", command->mCommand); + } + delete command; + waitTime = INT64_MAX; + } else { + waitTime = mAudioCommands[0]->mTime - curTime; + break; + } + } + // release delayed commands wake lock + if (mName != "" && mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + LOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.waitRelative(mLock, waitTime); + LOGV("AudioCommandThread() waking up"); + } + mLock.unlock(); + return false; +} + +status_t AudioPolicyService::AudioCommandThread::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "AudioCommandThread %p Dump\n", this); + result.append(buffer); + write(fd, result.string(), result.size()); + + bool locked = tryLock(mLock); + if (!locked) { + String8 result2(kCmdDeadlockedString); + write(fd, result2.string(), result2.size()); + } + + snprintf(buffer, SIZE, "- Commands:\n"); + result = String8(buffer); + result.append(" Command Time Wait pParam\n"); + for (int i = 0; i < (int)mAudioCommands.size(); i++) { + mAudioCommands[i]->dump(buffer, SIZE); + result.append(buffer); + } + result.append(" Last Command\n"); + mLastCommand.dump(buffer, SIZE); + result.append(buffer); + + write(fd, result.string(), result.size()); + + if (locked) mLock.unlock(); + + return NO_ERROR; +} + +void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stream) +{ + AudioCommand *command = new AudioCommand(); + command->mCommand = START_TONE; + ToneData *data = new ToneData(); + data->mType = type; + data->mStream = stream; + command->mParam = (void *)data; + command->mWaitStatus = false; + Mutex::Autolock _l(mLock); + insertCommand_l(command); + LOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); + mWaitWorkCV.signal(); +} + +void AudioPolicyService::AudioCommandThread::stopToneCommand() +{ + AudioCommand *command = new AudioCommand(); + command->mCommand = STOP_TONE; + command->mParam = NULL; + command->mWaitStatus = false; + Mutex::Autolock _l(mLock); + insertCommand_l(command); + LOGV("AudioCommandThread() adding tone stop"); + mWaitWorkCV.signal(); +} + +status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output, int delayMs) +{ + status_t status = NO_ERROR; + + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_VOLUME; + VolumeData *data = new VolumeData(); + data->mStream = stream; + data->mVolume = volume; + data->mIO = output; + command->mParam = data; + if (delayMs == 0) { + command->mWaitStatus = true; + } else { + command->mWaitStatus = false; + } + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", stream, volume, output); + mWaitWorkCV.signal(); + if (command->mWaitStatus) { + command->mCond.wait(mLock); + status = command->mStatus; + mWaitWorkCV.signal(); + } + return status; +} + +status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs) +{ + status_t status = NO_ERROR; + + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_PARAMETERS; + ParametersData *data = new ParametersData(); + data->mIO = ioHandle; + data->mKeyValuePairs = keyValuePairs; + command->mParam = data; + if (delayMs == 0) { + command->mWaitStatus = true; + } else { + command->mWaitStatus = false; + } + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", keyValuePairs.string(), ioHandle, delayMs); + mWaitWorkCV.signal(); + if (command->mWaitStatus) { + command->mCond.wait(mLock); + status = command->mStatus; + mWaitWorkCV.signal(); + } + return status; +} + +status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs) +{ + status_t status = NO_ERROR; + + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_VOICE_VOLUME; + VoiceVolumeData *data = new VoiceVolumeData(); + data->mVolume = volume; + command->mParam = data; + if (delayMs == 0) { + command->mWaitStatus = true; + } else { + command->mWaitStatus = false; + } + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + LOGV("AudioCommandThread() adding set voice volume volume %f", volume); + mWaitWorkCV.signal(); + if (command->mWaitStatus) { + command->mCond.wait(mLock); + status = command->mStatus; + mWaitWorkCV.signal(); + } + return status; +} + +// insertCommand_l() must be called with mLock held +void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs) +{ + ssize_t i; + Vector <AudioCommand *> removedCommands; + + command->mTime = systemTime() + milliseconds(delayMs); + + // acquire wake lock to make sure delayed commands are processed + if (mName != "" && mAudioCommands.isEmpty()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string()); + } + + // check same pending commands with later time stamps and eliminate them + for (i = mAudioCommands.size()-1; i >= 0; i--) { + AudioCommand *command2 = mAudioCommands[i]; + // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands + if (command2->mTime <= command->mTime) break; + if (command2->mCommand != command->mCommand) continue; + + switch (command->mCommand) { + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam; + ParametersData *data2 = (ParametersData *)command2->mParam; + if (data->mIO != data2->mIO) break; + LOGV("Comparing parameter command %s to new command %s", data2->mKeyValuePairs.string(), data->mKeyValuePairs.string()); + AudioParameter param = AudioParameter(data->mKeyValuePairs); + AudioParameter param2 = AudioParameter(data2->mKeyValuePairs); + for (size_t j = 0; j < param.size(); j++) { + String8 key; + String8 value; + param.getAt(j, key, value); + for (size_t k = 0; k < param2.size(); k++) { + String8 key2; + String8 value2; + param2.getAt(k, key2, value2); + if (key2 == key) { + param2.remove(key2); + LOGV("Filtering out parameter %s", key2.string()); + break; + } + } + } + // if all keys have been filtered out, remove the command. + // otherwise, update the key value pairs + if (param2.size() == 0) { + removedCommands.add(command2); + } else { + data2->mKeyValuePairs = param2.toString(); + } + } break; + + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam; + VolumeData *data2 = (VolumeData *)command2->mParam; + if (data->mIO != data2->mIO) break; + if (data->mStream != data2->mStream) break; + LOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream); + removedCommands.add(command2); + } break; + case START_TONE: + case STOP_TONE: + default: + break; + } + } + + // remove filtered commands + for (size_t j = 0; j < removedCommands.size(); j++) { + // removed commands always have time stamps greater than current command + for (size_t k = i + 1; k < mAudioCommands.size(); k++) { + if (mAudioCommands[k] == removedCommands[j]) { + LOGV("suppressing command: %d", mAudioCommands[k]->mCommand); + mAudioCommands.removeAt(k); + break; + } + } + } + removedCommands.clear(); + + // insert command at the right place according to its time stamp + LOGV("inserting command: %d at index %d, num commands %d", command->mCommand, (int)i+1, mAudioCommands.size()); + mAudioCommands.insertAt(command, i + 1); +} + +void AudioPolicyService::AudioCommandThread::exit() +{ + LOGV("AudioCommandThread::exit"); + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %02d %06d.%03d %01u %p\n", + mCommand, + (int)ns2s(mTime), + (int)ns2ms(mTime)%1000, + mWaitStatus, + mParam); +} + +}; // namespace android diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h new file mode 100644 index 0000000..a13d0bd --- /dev/null +++ b/services/audioflinger/AudioPolicyService.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2009 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_AUDIOPOLICYSERVICE_H +#define ANDROID_AUDIOPOLICYSERVICE_H + +#include <media/IAudioPolicyService.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include <media/ToneGenerator.h> +#include <utils/Vector.h> + +namespace android { + +class String8; + +// ---------------------------------------------------------------------------- + +class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, public IBinder::DeathRecipient +{ + +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // + // BnAudioPolicyService (see AudioPolicyInterface for method descriptions) + // + + virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address); + virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address); + virtual status_t setPhoneState(int state); + virtual status_t setRingerMode(uint32_t mode, uint32_t mask); + virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate = 0, + uint32_t format = AudioSystem::FORMAT_DEFAULT, + uint32_t channels = 0, + AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT); + virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(int inputSource, + uint32_t samplingRate = 0, + uint32_t format = AudioSystem::FORMAT_DEFAULT, + uint32_t channels = 0, + AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0); + virtual status_t startInput(audio_io_handle_t input); + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual status_t initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index); + virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + // + // AudioPolicyClientInterface + // + virtual audio_io_handle_t openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + AudioSystem::output_flags flags); + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); + virtual status_t closeOutput(audio_io_handle_t output); + virtual status_t suspendOutput(audio_io_handle_t output); + virtual status_t restoreOutput(audio_io_handle_t output); + virtual audio_io_handle_t openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics); + virtual status_t closeInput(audio_io_handle_t input); + virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0); + virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output); + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0); + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream); + virtual status_t stopTone(); + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + +private: + AudioPolicyService(); + virtual ~AudioPolicyService(); + + status_t dumpInternals(int fd); + + // Thread used for tone playback and to send audio config commands to audio flinger + // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because startTone() + // and stopTone() are normally called with mLock locked and requesting a tone start or stop will cause + // calls to AudioPolicyService and an attempt to lock mLock. + // For audio config commands, it is necessary because audio flinger requires that the calling process (user) + // has permission to modify audio settings. + class AudioCommandThread : public Thread { + class AudioCommand; + public: + + // commands for tone AudioCommand + enum { + START_TONE, + STOP_TONE, + SET_VOLUME, + SET_PARAMETERS, + SET_VOICE_VOLUME + }; + + AudioCommandThread (String8 name); + virtual ~AudioCommandThread(); + + status_t dump(int fd); + + // Thread virtuals + virtual void onFirstRef(); + virtual bool threadLoop(); + + void exit(); + void startToneCommand(int type = 0, int stream = 0); + void stopToneCommand(); + status_t volumeCommand(int stream, float volume, int output, int delayMs = 0); + status_t parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0); + status_t voiceVolumeCommand(float volume, int delayMs = 0); + void insertCommand_l(AudioCommand *command, int delayMs = 0); + + private: + // descriptor for requested tone playback event + class AudioCommand { + + public: + AudioCommand() + : mCommand(-1) {} + + void dump(char* buffer, size_t size); + + int mCommand; // START_TONE, STOP_TONE ... + nsecs_t mTime; // time stamp + Condition mCond; // condition for status return + status_t mStatus; // command status + bool mWaitStatus; // true if caller is waiting for status + void *mParam; // command parameter (ToneData, VolumeData, ParametersData) + }; + + class ToneData { + public: + int mType; // tone type (START_TONE only) + int mStream; // stream type (START_TONE only) + }; + + class VolumeData { + public: + int mStream; + float mVolume; + int mIO; + }; + + class ParametersData { + public: + int mIO; + String8 mKeyValuePairs; + }; + + class VoiceVolumeData { + public: + float mVolume; + }; + + Mutex mLock; + Condition mWaitWorkCV; + Vector <AudioCommand *> mAudioCommands; // list of pending commands + ToneGenerator *mpToneGenerator; // the tone generator + AudioCommand mLastCommand; // last processed command (used by dump) + String8 mName; // string used by wake lock fo delayed commands + }; + + // Internal dump utilities. + status_t dumpPermissionDenial(int fd); + + + Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing device + // connection stated our routing + AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager + sp <AudioCommandThread> mAudioCommandThread; // audio commands thread + sp <AudioCommandThread> mTonePlaybackThread; // tone playback thread +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYSERVICE_H + + + + + + + + diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp new file mode 100644 index 0000000..5dabacb --- /dev/null +++ b/services/audioflinger/AudioResampler.cpp @@ -0,0 +1,595 @@ +/* + * Copyright (C) 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 "AudioResampler" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include "AudioResampler.h" +#include "AudioResamplerSinc.h" +#include "AudioResamplerCubic.h" + +namespace android { + +#ifdef __ARM_ARCH_5E__ // optimized asm option + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 +#endif // __ARM_ARCH_5E__ +// ---------------------------------------------------------------------------- + +class AudioResamplerOrder1 : public AudioResampler { +public: + AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 15 bits avoids overflow + static const int kNumInterpBits = 15; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + + void init() {} + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); + void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); +#endif // ASM_ARM_RESAMP1 + + static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) { + return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits); + } + static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) { + *frac += inc; + *index += (size_t)(*frac >> kNumPhaseBits); + *frac &= kPhaseMask; + } + int mX0L; + int mX0R; +}; + +// ---------------------------------------------------------------------------- +AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality) { + + // can only create low quality resample now + AudioResampler* resampler; + + char value[PROPERTY_VALUE_MAX]; + if (property_get("af.resampler.quality", value, 0)) { + quality = atoi(value); + LOGD("forcing AudioResampler quality to %d", quality); + } + + if (quality == DEFAULT) + quality = LOW_QUALITY; + + switch (quality) { + default: + case LOW_QUALITY: + LOGV("Create linear Resampler"); + resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); + break; + case MED_QUALITY: + LOGV("Create cubic Resampler"); + resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); + break; + case HIGH_QUALITY: + LOGV("Create sinc Resampler"); + resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); + break; + } + + // initialize resampler + resampler->init(); + return resampler; +} + +AudioResampler::AudioResampler(int bitDepth, int inChannelCount, + int32_t sampleRate) : + mBitDepth(bitDepth), mChannelCount(inChannelCount), + mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), + mPhaseFraction(0) { + // sanity check on format + if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) { + LOGE("Unsupported sample format, %d bits, %d channels", bitDepth, + inChannelCount); + // LOG_ASSERT(0); + } + + // initialize common members + mVolume[0] = mVolume[1] = 0; + mBuffer.frameCount = 0; + + // save format for quick lookup + if (inChannelCount == 1) { + mFormat = MONO_16_BIT; + } else { + mFormat = STEREO_16_BIT; + } +} + +AudioResampler::~AudioResampler() { +} + +void AudioResampler::setSampleRate(int32_t inSampleRate) { + mInSampleRate = inSampleRate; + mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate); +} + +void AudioResampler::setVolume(int16_t left, int16_t right) { + // TODO: Implement anti-zipper filter + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + + while (outputIndex < outputSampleCount) { + + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + goto resampleStereo16_exit; + } + + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop + maxInIdx = mBuffer.frameCount - 2; + AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + out[outputIndex++] += vl * Interp(in[inputIndex*2-2], + in[inputIndex*2], phaseFraction); + out[outputIndex++] += vr * Interp(in[inputIndex*2-1], + in[inputIndex*2+1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleStereo16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; + goto resampleMono16_exit; + } + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + int32_t sample = Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); + maxInIdx = (int32_t)mBuffer.frameCount - 2; + AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + int32_t sample = Interp(in[inputIndex-1], in[inputIndex], + phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleMono16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + +/******************************************************************* +* +* AsmMono16Loop +* asm optimized monotonic loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) + + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" + // get parameters + " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, Out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 + // r13 sp + // r14 + + // the following loop works on 2 frames + + ".Y4L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y4L02\n" + +#define MO_ONE_FRAME \ + " add r0, r1, r7, asl #1\n" /* in + inputIndex */\ + " ldrsh r4, [r0]\n" /* in[inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ + " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r0, r0, r4\n" /* x0 - (..) */\ + " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ + " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */ + + MO_ONE_FRAME // frame 1 + MO_ONE_FRAME // frame 2 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y4L01\n" + ".Y4L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n" + ); +} + +/******************************************************************* +* +* AsmStereo16Loop +* asm optimized stereo loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" + // get parameters + " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 temporary + // r13 sp + // r14 + + ".Y5L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y5L02\n" + +#define ST_ONE_FRAME \ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ +\ + " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\ +\ + " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\ + " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r12, r12, r4\n" /* x0 - (..) */\ + " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\ + " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\ + " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r12, r12, lsl #2\n" /* <<2 */\ + " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\ + " add r12, r0, r12\n" /* x0 - (..) */\ + " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */ + + ST_ONE_FRAME // frame 1 + ST_ONE_FRAME // frame 1 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y5L01\n" + ".Y5L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n" + ); +} + +#endif // ASM_ARM_RESAMP1 + + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h new file mode 100644 index 0000000..2dfac76 --- /dev/null +++ b/services/audioflinger/AudioResampler.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 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_RESAMPLER_H +#define ANDROID_AUDIO_RESAMPLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "AudioBufferProvider.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResampler { +public: + // Determines quality of SRC. + // LOW_QUALITY: linear interpolator (1st order) + // MED_QUALITY: cubic interpolator (3rd order) + // HIGH_QUALITY: fixed multi-tap FIR (e.g. 48KHz->44.1KHz) + // NOTE: high quality SRC will only be supported for + // certain fixed rate conversions. Sample rate cannot be + // changed dynamically. + enum src_quality { + DEFAULT=0, + LOW_QUALITY=1, + MED_QUALITY=2, + HIGH_QUALITY=3 + }; + + static AudioResampler* create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality=DEFAULT); + + virtual ~AudioResampler(); + + virtual void init() = 0; + virtual void setSampleRate(int32_t inSampleRate); + virtual void setVolume(int16_t left, int16_t right); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) = 0; + +protected: + // number of bits for phase fraction - 30 bits allows nearly 2x downsampling + static const int kNumPhaseBits = 30; + + // phase mask for fraction + static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1; + + // multiplier to calculate fixed point phase increment + static const double kPhaseMultiplier = 1L << kNumPhaseBits; + + enum format {MONO_16_BIT, STEREO_16_BIT}; + AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate); + + // prevent copying + AudioResampler(const AudioResampler&); + AudioResampler& operator=(const AudioResampler&); + + int32_t mBitDepth; + int32_t mChannelCount; + int32_t mSampleRate; + int32_t mInSampleRate; + AudioBufferProvider::Buffer mBuffer; + union { + int16_t mVolume[2]; + uint32_t mVolumeRL; + }; + int16_t mTargetVolume[2]; + format mFormat; + size_t mInputIndex; + int32_t mPhaseIncrement; + uint32_t mPhaseFraction; +}; + +// ---------------------------------------------------------------------------- +} +; // namespace android + +#endif // ANDROID_AUDIO_RESAMPLER_H diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp new file mode 100644 index 0000000..1d247bd --- /dev/null +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 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. + */ + +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" +#include "AudioResamplerCubic.h" + +#define LOG_TAG "AudioSRC" + +namespace android { +// ---------------------------------------------------------------------------- + +void AudioResamplerCubic::init() { + memset(&left, 0, sizeof(state)); + memset(&right, 0, sizeof(state)); +} + +void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + out[outputIndex++] += vl * interp(&left, x); + out[outputIndex++] += vr * interp(&right, x); + // out[outputIndex++] += vr * in[inputIndex*2]; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + in = mBuffer.i16; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + + // advance sample state + advance(&left, in[inputIndex*2]); + advance(&right, in[inputIndex*2+1]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + sample = interp(&left, x); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + in = mBuffer.i16; + } + + // advance sample state + advance(&left, in[inputIndex]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h new file mode 100644 index 0000000..b72b62a --- /dev/null +++ b/services/audioflinger/AudioResamplerCubic.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 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_RESAMPLER_CUBIC_H +#define ANDROID_AUDIO_RESAMPLER_CUBIC_H + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResamplerCubic : public AudioResampler { +public: + AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 14 bits avoids overflow + static const int kNumInterpBits = 14; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + typedef struct { + int32_t a, b, c, y0, y1, y2, y3; + } state; + void init(); + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + static inline int32_t interp(state* p, int32_t x) { + return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1; + } + static inline void advance(state* p, int16_t in) { + p->y0 = p->y1; + p->y1 = p->y2; + p->y2 = p->y3; + p->y3 = in; + p->a = (3 * (p->y1 - p->y2) - p->y0 + p->y3) >> 1; + p->b = (p->y2 << 1) + p->y0 - (((5 * p->y1 + p->y3)) >> 1); + p->c = (p->y2 - p->y0) >> 1; + } + state left, right; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/ diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp new file mode 100644 index 0000000..9e5e254 --- /dev/null +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 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. + */ + +#include <string.h> +#include "AudioResamplerSinc.h" + +namespace android { +// ---------------------------------------------------------------------------- + + +/* + * These coeficients are computed with the "fir" utility found in + * tools/resampler_tools + * TODO: A good optimization would be to transpose this matrix, to take + * better advantage of the data-cache. + */ +const int32_t AudioResamplerSinc::mFirCoefsUp[] = { + 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, + 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, + 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, + 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, + 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, + 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, + 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, + 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +/* + * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz) + * It's possible to use the above coefficient for any down-sampling + * at the expense of a slower processing loop (we can interpolate + * these coefficient from the above by "Stretching" them in time). + */ +const int32_t AudioResamplerSinc::mFirCoefsDown[] = { + 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, + 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, + 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, + 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, + 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, + 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, + 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, + 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +// ---------------------------------------------------------------------------- + +static inline +int32_t mulRL(int left, int32_t in, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smultb %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(in>>16) * int16_t(vRL&0xFFFF); + } else { + return int16_t(in>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulAdd(int16_t in, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlawb %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * (v>>16); + // improved precision + // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlawb %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } else { + asm( "smlawt %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + (int16_t(inRL&0xFFFF) * (v>>16)); + //improved precision + // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); + } else { + return a + (int16_t(inRL>>16) * (v>>16)); + } +#endif +} + +// ---------------------------------------------------------------------------- + +AudioResamplerSinc::AudioResamplerSinc(int bitDepth, + int inChannelCount, int32_t sampleRate) + : AudioResampler(bitDepth, inChannelCount, sampleRate), + mState(0) +{ + /* + * Layout of the state buffer for 32 tap: + * + * "present" sample beginning of 2nd buffer + * v v + * 0 01 2 23 3 + * 0 F0 0 F0 F + * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] + * ^ ^ head + * + * p = past samples, convoluted with the (p)ositive side of sinc() + * n = future samples, convoluted with the (n)egative side of sinc() + * r = extra space for implementing the ring buffer + * + */ + + const size_t numCoefs = 2*halfNumCoefs; + const size_t stateSize = numCoefs * inChannelCount * 2; + mState = new int16_t[stateSize]; + memset(mState, 0, sizeof(int16_t)*stateSize); + mImpulse = mState + (halfNumCoefs-1)*inChannelCount; + mRingFull = mImpulse + (numCoefs+1)*inChannelCount; +} + +AudioResamplerSinc::~AudioResamplerSinc() +{ + delete [] mState; +} + +void AudioResamplerSinc::init() { +} + +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resample<1>(out, outFrameCount, provider); + break; + case 2: + resample<2>(out, outFrameCount, provider); + break; + } +} + + +template<int CHANNELS> +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + int16_t* impulse = mImpulse; + uint32_t vRL = mVolumeRL; + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + AudioBufferProvider::Buffer& buffer(mBuffer); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (buffer.frameCount == 0) { + buffer.frameCount = inFrameCount; + provider->getNextBuffer(&buffer); + if (buffer.raw == NULL) { + goto resample_exit; + } + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + // read one frame + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } else if (phaseIndex == 2) { + // read 2 frames + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + inputIndex++; + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + provider->releaseBuffer(&buffer); + } else { + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } + } + } + int16_t *in = buffer.i16; + const size_t frameCount = buffer.frameCount; + + // Always read-in the first samples from the input buffer + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; + + // handle boundary case + int32_t l, r; + while (outputIndex < outputSampleCount) { + filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); + out[outputIndex++] += 2 * mulRL(1, l, vRL); + out[outputIndex++] += 2 * mulRL(0, r, vRL); + + phaseFraction += phaseIncrement; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + inputIndex++; + if (inputIndex >= frameCount) + break; // need a new buffer + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } else if(phaseIndex == 2) { // maximum value + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 2 frames needed + // read first frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 1 frame needed + // read second frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } + } + + // if done with buffer, save samples + if (inputIndex >= frameCount) { + inputIndex -= frameCount; + provider->releaseBuffer(&buffer); + } + } + +resample_exit: + mImpulse = impulse; + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +template<int CHANNELS> +/*** +* read() +* +* This function reads only one frame from input buffer and writes it in +* state buffer +* +**/ +void AudioResamplerSinc::read( + int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex) +{ + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + impulse += CHANNELS; + phaseFraction -= 1LU<<kNumPhaseBits; + if (impulse >= mRingFull) { + const size_t stateSize = (halfNumCoefs*2)*CHANNELS; + memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); + impulse -= stateSize; + } + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; +} + +template<int CHANNELS> +void AudioResamplerSinc::filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples) +{ + // compute the index of the coefficient on the positive side and + // negative side + uint32_t indexP = (phase & cMask) >> cShift; + uint16_t lerpP = (phase & pMask) >> pShift; + uint32_t indexN = (-phase & cMask) >> cShift; + uint16_t lerpN = (-phase & pMask) >> pShift; + if ((indexP == 0) && (lerpP == 0)) { + indexN = cMask >> cShift; + lerpN = pMask >> pShift; + } + + l = 0; + r = 0; + int32_t const* coefs = mFirCoefs; + int16_t const *sP = samples; + int16_t const *sN = samples+CHANNELS; + for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) { + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + } +} + +template<int CHANNELS> +void AudioResamplerSinc::interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples) +{ + int32_t c0 = coefs[0]; + int32_t c1 = coefs[1]; + int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); + if (CHANNELS == 2) { + uint32_t rl = *reinterpret_cast<uint32_t const*>(samples); + l = mulAddRL(1, rl, sinc, l); + r = mulAddRL(0, rl, sinc, r); + } else { + r = l = mulAdd(samples[0], sinc, l); + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h new file mode 100644 index 0000000..e6cb90b --- /dev/null +++ b/services/audioflinger/AudioResamplerSinc.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 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_RESAMPLER_SINC_H +#define ANDROID_AUDIO_RESAMPLER_SINC_H + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioResamplerSinc : public AudioResampler { +public: + AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + + ~AudioResamplerSinc(); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + void init(); + + template<int CHANNELS> + void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + + template<int CHANNELS> + inline void filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples); + + template<int CHANNELS> + inline void interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples); + + template<int CHANNELS> + inline void read(int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex); + + int16_t *mState; + int16_t *mImpulse; + int16_t *mRingFull; + + int32_t const * mFirCoefs; + static const int32_t mFirCoefsDown[]; + static const int32_t mFirCoefsUp[]; + + // ---------------------------------------------------------------------------- + static const int32_t RESAMPLE_FIR_NUM_COEF = 8; + static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4; + + // we have 16 coefs samples per zero-crossing + static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4 + static const int cShift = kNumPhaseBits - coefsBits; // 26 + static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000 + + // and we use 15 bits to interpolate between these samples + // this cannot change because the mul below rely on it. + static const int pLerpBits = 15; + static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11 + static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11 + + // number of zero-crossing on each side + static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/ diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk new file mode 100644 index 0000000..df5c166 --- /dev/null +++ b/services/camera/libcameraservice/Android.mk @@ -0,0 +1,71 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want +# the camera service to use the fake camera. For emulator or simulator builds, +# we always use the fake camera. + +ifeq ($(USE_CAMERA_STUB),) +USE_CAMERA_STUB:=false +ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),) +USE_CAMERA_STUB:=true +endif #libcamerastub +endif + +ifeq ($(USE_CAMERA_STUB),true) +# +# libcamerastub +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraHardwareStub.cpp \ + FakeCamera.cpp + +LOCAL_MODULE:= libcamerastub + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_CFLAGS += -DSINGLE_PROCESS +endif + +LOCAL_SHARED_LIBRARIES:= libui + +include $(BUILD_STATIC_LIBRARY) +endif # USE_CAMERA_STUB + +# +# libcameraservice +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + libutils \ + libbinder \ + libcutils \ + libmedia \ + libcamera_client \ + libsurfaceflinger_client + +LOCAL_MODULE:= libcameraservice + +LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\" + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_CFLAGS += -DSINGLE_PROCESS +endif + +ifeq ($(USE_CAMERA_STUB), true) +LOCAL_STATIC_LIBRARIES += libcamerastub +LOCAL_CFLAGS += -include CameraHardwareStub.h +else +LOCAL_SHARED_LIBRARIES += libcamera +endif + +include $(BUILD_SHARED_LIBRARY) + diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp new file mode 100644 index 0000000..8b66389 --- /dev/null +++ b/services/camera/libcameraservice/CameraHardwareStub.cpp @@ -0,0 +1,402 @@ +/* +** +** Copyright 2008, 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 "CameraHardwareStub" +#include <utils/Log.h> + +#include "CameraHardwareStub.h" +#include <utils/threads.h> +#include <fcntl.h> +#include <sys/mman.h> + +#include "CannedJpeg.h" + +namespace android { + +CameraHardwareStub::CameraHardwareStub() + : mParameters(), + mPreviewHeap(0), + mRawHeap(0), + mFakeCamera(0), + mPreviewFrameSize(0), + mNotifyCb(0), + mDataCb(0), + mDataCbTimestamp(0), + mCallbackCookie(0), + mMsgEnabled(0), + mCurrentPreviewFrame(0) +{ + initDefaultParameters(); +} + +void CameraHardwareStub::initDefaultParameters() +{ + CameraParameters p; + + p.set("preview-size-values","320x240"); + p.setPreviewSize(320, 240); + p.setPreviewFrameRate(15); + p.setPreviewFormat("yuv422sp"); + + p.set("picture-size-values", "320x240"); + p.setPictureSize(320, 240); + p.setPictureFormat("jpeg"); + + if (setParameters(p) != NO_ERROR) { + LOGE("Failed to set default parameters?!"); + } +} + +void CameraHardwareStub::initHeapLocked() +{ + // Create raw heap. + int picture_width, picture_height; + mParameters.getPictureSize(&picture_width, &picture_height); + mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height); + + int preview_width, preview_height; + mParameters.getPreviewSize(&preview_width, &preview_height); + LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); + + // Note that we enforce yuv422 in setParameters(). + int how_big = preview_width * preview_height * 2; + + // If we are being reinitialized to the same size as before, no + // work needs to be done. + if (how_big == mPreviewFrameSize) + return; + + mPreviewFrameSize = how_big; + + // Make a new mmap'ed heap that can be shared across processes. + // use code below to test with pmem + mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount); + // Make an IMemory for each frame so that we can reuse them in callbacks. + for (int i = 0; i < kBufferCount; i++) { + mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize); + } + + // Recreate the fake camera to reflect the current size. + delete mFakeCamera; + mFakeCamera = new FakeCamera(preview_width, preview_height); +} + +CameraHardwareStub::~CameraHardwareStub() +{ + delete mFakeCamera; + mFakeCamera = 0; // paranoia + singleton.clear(); +} + +sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const +{ + return mPreviewHeap; +} + +sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const +{ + return mRawHeap; +} + +void CameraHardwareStub::setCallbacks(notify_callback notify_cb, + data_callback data_cb, + data_callback_timestamp data_cb_timestamp, + void* user) +{ + Mutex::Autolock lock(mLock); + mNotifyCb = notify_cb; + mDataCb = data_cb; + mDataCbTimestamp = data_cb_timestamp; + mCallbackCookie = user; +} + +void CameraHardwareStub::enableMsgType(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + mMsgEnabled |= msgType; +} + +void CameraHardwareStub::disableMsgType(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + mMsgEnabled &= ~msgType; +} + +bool CameraHardwareStub::msgTypeEnabled(int32_t msgType) +{ + Mutex::Autolock lock(mLock); + return (mMsgEnabled & msgType); +} + +// --------------------------------------------------------------------------- + +int CameraHardwareStub::previewThread() +{ + mLock.lock(); + // the attributes below can change under our feet... + + int previewFrameRate = mParameters.getPreviewFrameRate(); + + // Find the offset within the heap of the current buffer. + ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize; + + sp<MemoryHeapBase> heap = mPreviewHeap; + + // this assumes the internal state of fake camera doesn't change + // (or is thread safe) + FakeCamera* fakeCamera = mFakeCamera; + + sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame]; + + mLock.unlock(); + + // TODO: here check all the conditions that could go wrong + if (buffer != 0) { + // Calculate how long to wait between frames. + int delay = (int)(1000000.0f / float(previewFrameRate)); + + // This is always valid, even if the client died -- the memory + // is still mapped in our process. + void *base = heap->base(); + + // Fill the current frame with the fake camera. + uint8_t *frame = ((uint8_t *)base) + offset; + fakeCamera->getNextFrameAsYuv422(frame); + + //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame); + + // Notify the client of a new frame. + if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) + mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie); + + // Advance the buffer pointer. + mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount; + + // Wait for it... + usleep(delay); + } + + return NO_ERROR; +} + +status_t CameraHardwareStub::startPreview() +{ + Mutex::Autolock lock(mLock); + if (mPreviewThread != 0) { + // already running + return INVALID_OPERATION; + } + mPreviewThread = new PreviewThread(this); + return NO_ERROR; +} + +void CameraHardwareStub::stopPreview() +{ + sp<PreviewThread> previewThread; + + { // scope for the lock + Mutex::Autolock lock(mLock); + previewThread = mPreviewThread; + } + + // don't hold the lock while waiting for the thread to quit + if (previewThread != 0) { + previewThread->requestExitAndWait(); + } + + Mutex::Autolock lock(mLock); + mPreviewThread.clear(); +} + +bool CameraHardwareStub::previewEnabled() { + return mPreviewThread != 0; +} + +status_t CameraHardwareStub::startRecording() +{ + return UNKNOWN_ERROR; +} + +void CameraHardwareStub::stopRecording() +{ +} + +bool CameraHardwareStub::recordingEnabled() +{ + return false; +} + +void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem) +{ +} + +// --------------------------------------------------------------------------- + +int CameraHardwareStub::beginAutoFocusThread(void *cookie) +{ + CameraHardwareStub *c = (CameraHardwareStub *)cookie; + return c->autoFocusThread(); +} + +int CameraHardwareStub::autoFocusThread() +{ + if (mMsgEnabled & CAMERA_MSG_FOCUS) + mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie); + return NO_ERROR; +} + +status_t CameraHardwareStub::autoFocus() +{ + Mutex::Autolock lock(mLock); + if (createThread(beginAutoFocusThread, this) == false) + return UNKNOWN_ERROR; + return NO_ERROR; +} + +status_t CameraHardwareStub::cancelAutoFocus() +{ + return NO_ERROR; +} + +/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie) +{ + CameraHardwareStub *c = (CameraHardwareStub *)cookie; + return c->pictureThread(); +} + +int CameraHardwareStub::pictureThread() +{ + if (mMsgEnabled & CAMERA_MSG_SHUTTER) + mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie); + + if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) { + //FIXME: use a canned YUV image! + // In the meantime just make another fake camera picture. + int w, h; + mParameters.getPictureSize(&w, &h); + sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h); + FakeCamera cam(w, h); + cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base()); + mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie); + } + + if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { + sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize); + sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize); + memcpy(heap->base(), kCannedJpeg, kCannedJpegSize); + mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie); + } + return NO_ERROR; +} + +status_t CameraHardwareStub::takePicture() +{ + stopPreview(); + if (createThread(beginPictureThread, this) == false) + return -1; + return NO_ERROR; +} + +status_t CameraHardwareStub::cancelPicture() +{ + return NO_ERROR; +} + +status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + AutoMutex lock(&mLock); + if (mFakeCamera != 0) { + mFakeCamera->dump(fd); + mParameters.dump(fd, args); + snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false"); + result.append(buffer); + } else { + result.append("No camera client yet.\n"); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t CameraHardwareStub::setParameters(const CameraParameters& params) +{ + Mutex::Autolock lock(mLock); + // XXX verify params + + if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) { + LOGE("Only yuv422sp preview is supported"); + return -1; + } + + if (strcmp(params.getPictureFormat(), "jpeg") != 0) { + LOGE("Only jpeg still pictures are supported"); + return -1; + } + + int w, h; + params.getPictureSize(&w, &h); + if (w != kCannedJpegWidth && h != kCannedJpegHeight) { + LOGE("Still picture size must be size of canned JPEG (%dx%d)", + kCannedJpegWidth, kCannedJpegHeight); + return -1; + } + + mParameters = params; + initHeapLocked(); + + return NO_ERROR; +} + +CameraParameters CameraHardwareStub::getParameters() const +{ + Mutex::Autolock lock(mLock); + return mParameters; +} + +status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1, + int32_t arg2) +{ + return BAD_VALUE; +} + +void CameraHardwareStub::release() +{ +} + +wp<CameraHardwareInterface> CameraHardwareStub::singleton; + +sp<CameraHardwareInterface> CameraHardwareStub::createInstance() +{ + if (singleton != 0) { + sp<CameraHardwareInterface> hardware = singleton.promote(); + if (hardware != 0) { + return hardware; + } + } + sp<CameraHardwareInterface> hardware(new CameraHardwareStub()); + singleton = hardware; + return hardware; +} + +extern "C" sp<CameraHardwareInterface> openCameraHardware() +{ + return CameraHardwareStub::createInstance(); +} + +}; // namespace android diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h new file mode 100644 index 0000000..957813a4 --- /dev/null +++ b/services/camera/libcameraservice/CameraHardwareStub.h @@ -0,0 +1,135 @@ +/* +** +** Copyright 2008, 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_CAMERA_HARDWARE_STUB_H +#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H + +#include "FakeCamera.h" +#include <utils/threads.h> +#include <camera/CameraHardwareInterface.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <utils/threads.h> + +namespace android { + +class CameraHardwareStub : public CameraHardwareInterface { +public: + virtual sp<IMemoryHeap> getPreviewHeap() const; + virtual sp<IMemoryHeap> getRawHeap() const; + + virtual void setCallbacks(notify_callback notify_cb, + data_callback data_cb, + data_callback_timestamp data_cb_timestamp, + void* user); + + virtual void enableMsgType(int32_t msgType); + virtual void disableMsgType(int32_t msgType); + virtual bool msgTypeEnabled(int32_t msgType); + + virtual status_t startPreview(); + virtual void stopPreview(); + virtual bool previewEnabled(); + + virtual status_t startRecording(); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp<IMemory>& mem); + + virtual status_t autoFocus(); + virtual status_t cancelAutoFocus(); + virtual status_t takePicture(); + virtual status_t cancelPicture(); + virtual status_t dump(int fd, const Vector<String16>& args) const; + virtual status_t setParameters(const CameraParameters& params); + virtual CameraParameters getParameters() const; + virtual status_t sendCommand(int32_t command, int32_t arg1, + int32_t arg2); + virtual void release(); + + static sp<CameraHardwareInterface> createInstance(); + +private: + CameraHardwareStub(); + virtual ~CameraHardwareStub(); + + static wp<CameraHardwareInterface> singleton; + + static const int kBufferCount = 4; + + class PreviewThread : public Thread { + CameraHardwareStub* mHardware; + public: + PreviewThread(CameraHardwareStub* hw) : +#ifdef SINGLE_PROCESS + // In single process mode this thread needs to be a java thread, + // since we won't be calling through the binder. + Thread(true), +#else + Thread(false), +#endif + mHardware(hw) { } + virtual void onFirstRef() { + run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY); + } + virtual bool threadLoop() { + mHardware->previewThread(); + // loop until we need to quit + return true; + } + }; + + void initDefaultParameters(); + void initHeapLocked(); + + int previewThread(); + + static int beginAutoFocusThread(void *cookie); + int autoFocusThread(); + + static int beginPictureThread(void *cookie); + int pictureThread(); + + mutable Mutex mLock; + + CameraParameters mParameters; + + sp<MemoryHeapBase> mPreviewHeap; + sp<MemoryHeapBase> mRawHeap; + sp<MemoryBase> mBuffers[kBufferCount]; + + FakeCamera *mFakeCamera; + bool mPreviewRunning; + int mPreviewFrameSize; + + // protected by mLock + sp<PreviewThread> mPreviewThread; + + notify_callback mNotifyCb; + data_callback mDataCb; + data_callback_timestamp mDataCbTimestamp; + void *mCallbackCookie; + + int32_t mMsgEnabled; + + // only used from PreviewThread + int mCurrentPreviewFrame; +}; + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp new file mode 100644 index 0000000..00bd54e --- /dev/null +++ b/services/camera/libcameraservice/CameraService.cpp @@ -0,0 +1,1417 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** 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 "CameraService" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/Errors.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <camera/ICameraService.h> +#include <surfaceflinger/ISurface.h> +#include <ui/Overlay.h> + +#include <hardware/hardware.h> + +#include <media/mediaplayer.h> +#include <media/AudioSystem.h> +#include "CameraService.h" + +#include <cutils/atomic.h> + +namespace android { + +extern "C" { +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +} + +// When you enable this, as well as DEBUG_REFS=1 and +// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all +// references to the CameraService::Client in order to catch the case where the +// client is being destroyed while a callback from the CameraHardwareInterface +// is outstanding. This is a serious bug because if we make another call into +// CameraHardwreInterface that itself triggers a callback, we will deadlock. + +#define DEBUG_CLIENT_REFERENCES 0 + +#define PICTURE_TIMEOUT seconds(5) + +#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */ +#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0 +#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0 +#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0 + +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE +static int debug_frame_cnt; +#endif + +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +// ---------------------------------------------------------------------------- + +void CameraService::instantiate() { + defaultServiceManager()->addService( + String16("media.camera"), new CameraService()); +} + +// ---------------------------------------------------------------------------- + +CameraService::CameraService() : + BnCameraService() +{ + LOGI("CameraService started: pid=%d", getpid()); + mUsers = 0; +} + +CameraService::~CameraService() +{ + if (mClient != 0) { + LOGE("mClient was still connected in destructor!"); + } +} + +sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) +{ + int callingPid = getCallingPid(); + LOGV("CameraService::connect E (pid %d, client %p)", callingPid, + cameraClient->asBinder().get()); + + Mutex::Autolock lock(mServiceLock); + sp<Client> client; + if (mClient != 0) { + sp<Client> currentClient = mClient.promote(); + if (currentClient != 0) { + sp<ICameraClient> currentCameraClient(currentClient->getCameraClient()); + if (cameraClient->asBinder() == currentCameraClient->asBinder()) { + // This is the same client reconnecting... + LOGV("CameraService::connect X (pid %d, same client %p) is reconnecting...", + callingPid, cameraClient->asBinder().get()); + return currentClient; + } else { + // It's another client... reject it + LOGV("CameraService::connect X (pid %d, new client %p) rejected. " + "(old pid %d, old client %p)", + callingPid, cameraClient->asBinder().get(), + currentClient->mClientPid, currentCameraClient->asBinder().get()); + if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) { + LOGV("The old client is dead!"); + } + return client; + } + } else { + // can't promote, the previous client has died... + LOGV("New client (pid %d) connecting, old reference was dangling...", + callingPid); + mClient.clear(); + } + } + + if (mUsers > 0) { + LOGV("Still have client, rejected"); + return client; + } + + // create a new Client object + client = new Client(this, cameraClient, callingPid); + mClient = client; +#if DEBUG_CLIENT_REFERENCES + // Enable tracking for this object, and track increments and decrements of + // the refcount. + client->trackMe(true, true); +#endif + LOGV("CameraService::connect X"); + return client; +} + +void CameraService::removeClient(const sp<ICameraClient>& cameraClient) +{ + int callingPid = getCallingPid(); + + // Declare this outside the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp<Client> client; + + Mutex::Autolock lock(mServiceLock); + + if (mClient == 0) { + // This happens when we have already disconnected. + LOGV("removeClient (pid %d): already disconnected", callingPid); + return; + } + + // Promote mClient. It can fail if we are called from this path: + // Client::~Client() -> disconnect() -> removeClient(). + client = mClient.promote(); + if (client == 0) { + LOGV("removeClient (pid %d): no more strong reference", callingPid); + mClient.clear(); + return; + } + + if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) { + // ugh! that's not our client!! + LOGW("removeClient (pid %d): mClient doesn't match!", callingPid); + } else { + // okay, good, forget about mClient + mClient.clear(); + } + + LOGV("removeClient (pid %d) done", callingPid); +} + +// The reason we need this count 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 +// destructor has not been run, 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::incUsers() { + android_atomic_inc(&mUsers); +} + +void CameraService::decUsers() { + android_atomic_dec(&mUsers); +} + +static sp<MediaPlayer> newMediaPlayer(const char *file) +{ + sp<MediaPlayer> mp = new MediaPlayer(); + if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) { + mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE); + mp->prepare(); + } else { + mp.clear(); + LOGE("Failed to load CameraService sounds."); + } + return mp; +} + +CameraService::Client::Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, pid_t clientPid) +{ + int callingPid = getCallingPid(); + LOGV("Client::Client E (pid %d)", callingPid); + mCameraService = cameraService; + mCameraClient = cameraClient; + mClientPid = clientPid; + mHardware = openCameraHardware(); + mUseOverlay = mHardware->useOverlay(); + + mHardware->setCallbacks(notifyCallback, + dataCallback, + dataCallbackTimestamp, + mCameraService.get()); + + // Enable zoom, error, and focus messages by default + mHardware->enableMsgType(CAMERA_MSG_ERROR | + CAMERA_MSG_ZOOM | + CAMERA_MSG_FOCUS); + + mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg"); + mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg"); + mOverlayW = 0; + mOverlayH = 0; + + // Callback is disabled by default + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + mOrientation = 0; + cameraService->incUsers(); + LOGV("Client::Client X (pid %d)", callingPid); +} + +status_t CameraService::Client::checkPid() +{ + int callingPid = getCallingPid(); + if (mClientPid == callingPid) return NO_ERROR; + LOGW("Attempt to use locked camera (client %p) from different process " + " (old pid %d, new pid %d)", + getCameraClient()->asBinder().get(), mClientPid, callingPid); + return -EBUSY; +} + +status_t CameraService::Client::lock() +{ + int callingPid = getCallingPid(); + LOGV("lock from pid %d (mClientPid %d)", callingPid, mClientPid); + Mutex::Autolock _l(mLock); + // lock camera to this client if the the camera is unlocked + if (mClientPid == 0) { + mClientPid = callingPid; + return NO_ERROR; + } + // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise + return checkPid(); +} + +status_t CameraService::Client::unlock() +{ + int callingPid = getCallingPid(); + LOGV("unlock from pid %d (mClientPid %d)", callingPid, mClientPid); + Mutex::Autolock _l(mLock); + // allow anyone to use camera + status_t result = checkPid(); + if (result == NO_ERROR) { + mClientPid = 0; + LOGV("clear mCameraClient (pid %d)", callingPid); + // we need to remove the reference so that when app goes + // away, the reference count goes to 0. + mCameraClient.clear(); + } + return result; +} + +status_t CameraService::Client::connect(const sp<ICameraClient>& client) +{ + int callingPid = getCallingPid(); + + // connect a new process to the camera + LOGV("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get()); + + // I hate this hack, but things get really ugly when the media recorder + // service is handing back the camera to the app. The ICameraClient + // destructor will be called during the same IPC, making it look like + // the remote client is trying to disconnect. This hack temporarily + // sets the mClientPid to an invalid pid to prevent the hardware from + // being torn down. + { + + // hold a reference to the old client or we will deadlock if the client is + // in the same process and we hold the lock when we remove the reference + sp<ICameraClient> oldClient; + { + Mutex::Autolock _l(mLock); + if (mClientPid != 0 && checkPid() != NO_ERROR) { + LOGW("Tried to connect to locked camera (old pid %d, new pid %d)", + mClientPid, callingPid); + return -EBUSY; + } + oldClient = mCameraClient; + + // did the client actually change? + if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) { + LOGV("Connect to the same client"); + return NO_ERROR; + } + + mCameraClient = client; + mClientPid = -1; + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + LOGV("Connect to the new client (pid %d, client %p)", + callingPid, mCameraClient->asBinder().get()); + } + + } + // the old client destructor is called when oldClient goes out of scope + // now we set the new PID to lock the interface again + mClientPid = callingPid; + + return NO_ERROR; +} + +#if HAVE_ANDROID_OS +static void *unregister_surface(void *arg) +{ + ISurface *surface = (ISurface *)arg; + surface->unregisterBuffers(); + IPCThreadState::self()->flushCommands(); + return NULL; +} +#endif + +CameraService::Client::~Client() +{ + int callingPid = getCallingPid(); + + // tear down client + LOGV("Client::~Client E (pid %d, client %p)", + callingPid, getCameraClient()->asBinder().get()); + if (mSurface != 0 && !mUseOverlay) { +#if HAVE_ANDROID_OS + pthread_t thr; + // We unregister the buffers in a different thread because binder does + // not let us make sychronous transactions in a binder destructor (that + // is, upon our reaching a refcount of zero.) + pthread_create(&thr, NULL, + unregister_surface, + mSurface.get()); + pthread_join(thr, NULL); +#else + mSurface->unregisterBuffers(); +#endif + } + + if (mMediaPlayerBeep.get() != NULL) { + mMediaPlayerBeep->disconnect(); + mMediaPlayerBeep.clear(); + } + if (mMediaPlayerClick.get() != NULL) { + mMediaPlayerClick->disconnect(); + mMediaPlayerClick.clear(); + } + + // make sure we tear down the hardware + mClientPid = callingPid; + disconnect(); + LOGV("Client::~Client X (pid %d)", mClientPid); +} + +void CameraService::Client::disconnect() +{ + int callingPid = getCallingPid(); + + LOGV("Client::disconnect() E (pid %d client %p)", + callingPid, getCameraClient()->asBinder().get()); + + Mutex::Autolock lock(mLock); + if (mClientPid <= 0) { + LOGV("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); + return; + } + if (checkPid() != NO_ERROR) { + LOGV("Different client - don't disconnect"); + return; + } + + // Make sure disconnect() is done once and once only, whether it is called + // from the user directly, or called by the destructor. + if (mHardware == 0) return; + + LOGV("hardware teardown"); + // Before destroying mHardware, we must make sure it's in the + // idle state. + mHardware->stopPreview(); + // Cancel all picture callbacks. + mHardware->disableMsgType(CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_COMPRESSED_IMAGE); + mHardware->cancelPicture(); + // Turn off remaining messages. + mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS); + // Release the hardware resources. + mHardware->release(); + // Release the held overlay resources. + if (mUseOverlay) + { + mOverlayRef = 0; + } + mHardware.clear(); + + mCameraService->removeClient(mCameraClient); + mCameraService->decUsers(); + + LOGV("Client::disconnect() X (pid %d)", callingPid); +} + +// pass the buffered ISurface to the camera service +status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) +{ + LOGV("setPreviewDisplay(%p) (pid %d)", + ((surface == NULL) ? NULL : surface.get()), getCallingPid()); + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + Mutex::Autolock surfaceLock(mSurfaceLock); + result = NO_ERROR; + // asBinder() is safe on NULL (returns NULL) + if (surface->asBinder() != mSurface->asBinder()) { + if (mSurface != 0) { + LOGV("clearing old preview surface %p", mSurface.get()); + if ( !mUseOverlay) + { + mSurface->unregisterBuffers(); + } + else + { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay( dummy ); + } + } + mSurface = surface; + mOverlayRef = 0; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); + } + } + } + return result; +} + +// set the preview callback flag to affect how the received frames from +// preview are handled. +void CameraService::Client::setPreviewCallbackFlag(int callback_flag) +{ + LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + mPreviewCallbackFlag = callback_flag; + + if(mUseOverlay) { + if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) + mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + else + mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } +} + +// start preview mode +status_t CameraService::Client::startCameraMode(camera_mode mode) +{ + int callingPid = getCallingPid(); + + LOGV("startCameraMode(%d) (pid %d)", mode, callingPid); + + /* we cannot call into mHardware with mLock held because + * mHardware has callbacks onto us which acquire this lock + */ + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + switch(mode) { + case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("setPreviewDisplay must be called before startRecordingMode."); + return INVALID_OPERATION; + } + return startRecordingMode(); + + default: // CAMERA_PREVIEW_MODE + if (mSurface == 0) { + LOGV("mSurface is not set yet."); + } + return startPreviewMode(); + } +} + +status_t CameraService::Client::startRecordingMode() +{ + LOGV("startRecordingMode (pid %d)", getCallingPid()); + + status_t ret = UNKNOWN_ERROR; + + // if preview has not been started, start preview first + if (!mHardware->previewEnabled()) { + ret = startPreviewMode(); + if (ret != NO_ERROR) { + return ret; + } + } + + // if recording has been enabled, nothing needs to be done + if (mHardware->recordingEnabled()) { + return NO_ERROR; + } + + // start recording mode + ret = mHardware->startRecording(); + if (ret != NO_ERROR) { + LOGE("mHardware->startRecording() failed with status %d", ret); + } + return ret; +} + +status_t CameraService::Client::setOverlay() +{ + LOGV("setOverlay"); + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + if ( w != mOverlayW || h != mOverlayH ) + { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay( dummy ); + mOverlayRef = 0; + } + + status_t ret = NO_ERROR; + if (mSurface != 0) { + if (mOverlayRef.get() == NULL) { + + // FIXME: + // Surfaceflinger may hold onto the previous overlay reference for some + // time after we try to destroy it. retry a few times. In the future, we + // should make the destroy call block, or possibly specify that we can + // wait in the createOverlay call if the previous overlay is in the + // process of being destroyed. + for (int retry = 0; retry < 50; ++retry) { + mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT, + mOrientation); + if (mOverlayRef != NULL) break; + LOGW("Overlay create failed - retrying"); + usleep(20000); + } + if ( mOverlayRef.get() == NULL ) + { + LOGE("Overlay Creation Failed!"); + return -EINVAL; + } + ret = mHardware->setOverlay(new Overlay(mOverlayRef)); + } + } else { + ret = mHardware->setOverlay(NULL); + } + if (ret != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", ret); + } + + mOverlayW = w; + mOverlayH = h; + + return ret; +} + +status_t CameraService::Client::registerPreviewBuffers() +{ + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + // don't use a hardcoded format here + ISurface::BufferHeap buffers(w, h, w, h, + HAL_PIXEL_FORMAT_YCrCb_420_SP, + mOrientation, + 0, + mHardware->getPreviewHeap()); + + status_t ret = mSurface->registerBuffers(buffers); + if (ret != NO_ERROR) { + LOGE("registerBuffers failed with status %d", ret); + } + return ret; +} + +status_t CameraService::Client::startPreviewMode() +{ + LOGV("startPreviewMode (pid %d)", getCallingPid()); + + // if preview has been enabled, nothing needs to be done + if (mHardware->previewEnabled()) { + return NO_ERROR; + } + + // start preview mode +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + debug_frame_cnt = 0; +#endif + status_t ret = NO_ERROR; + + if (mUseOverlay) { + // If preview display has been set, set overlay now. + if (mSurface != 0) { + ret = setOverlay(); + } + if (ret != NO_ERROR) return ret; + ret = mHardware->startPreview(); + } else { + mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + ret = mHardware->startPreview(); + if (ret != NO_ERROR) return ret; + // If preview display has been set, register preview buffers now. + if (mSurface != 0) { + // Unregister here because the surface registered with raw heap. + mSurface->unregisterBuffers(); + ret = registerPreviewBuffers(); + } + } + return ret; +} + +status_t CameraService::Client::startPreview() +{ + LOGV("startPreview (pid %d)", getCallingPid()); + + return startCameraMode(CAMERA_PREVIEW_MODE); +} + +status_t CameraService::Client::startRecording() +{ + LOGV("startRecording (pid %d)", getCallingPid()); + + if (mMediaPlayerBeep.get() != NULL) { + // do not play record jingle if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + mMediaPlayerBeep->seekTo(0); + mMediaPlayerBeep->start(); + } + } + + mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME); + + return startCameraMode(CAMERA_RECORDING_MODE); +} + +// stop preview mode +void CameraService::Client::stopPreview() +{ + LOGV("stopPreview (pid %d)", getCallingPid()); + + // hold main lock during state transition + { + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->stopPreview(); + mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + LOGV("stopPreview(), hardware stopped OK"); + + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); + } + } + + // hold preview buffer lock + { + Mutex::Autolock lock(mPreviewLock); + mPreviewBuffer.clear(); + } +} + +// stop recording mode +void CameraService::Client::stopRecording() +{ + LOGV("stopRecording (pid %d)", getCallingPid()); + + // hold main lock during state transition + { + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + if (mMediaPlayerBeep.get() != NULL) { + mMediaPlayerBeep->seekTo(0); + mMediaPlayerBeep->start(); + } + + mHardware->stopRecording(); + mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME); + LOGV("stopRecording(), hardware stopped OK"); + } + + // hold preview buffer lock + { + Mutex::Autolock lock(mPreviewLock); + mPreviewBuffer.clear(); + } +} + +// release a recording frame +void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) +{ + Mutex::Autolock lock(mLock); + if (checkPid() != NO_ERROR) return; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return; + } + + mHardware->releaseRecordingFrame(mem); +} + +bool CameraService::Client::previewEnabled() +{ + Mutex::Autolock lock(mLock); + if (mHardware == 0) return false; + return mHardware->previewEnabled(); +} + +bool CameraService::Client::recordingEnabled() +{ + Mutex::Autolock lock(mLock); + if (mHardware == 0) return false; + return mHardware->recordingEnabled(); +} + +// Safely retrieves a strong pointer to the client during a hardware callback. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) +{ + sp<Client> client = 0; + CameraService *service = static_cast<CameraService*>(user); + if (service != NULL) { + Mutex::Autolock ourLock(service->mServiceLock); + if (service->mClient != 0) { + client = service->mClient.promote(); + if (client == 0) { + LOGE("getClientFromCookie: client appears to have died"); + service->mClient.clear(); + } + } else { + LOGE("getClientFromCookie: got callback but client was NULL"); + } + } + return client; +} + + +#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \ + DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \ + DEBUG_DUMP_PREVIEW_FRAME_TO_FILE +static void dump_to_file(const char *fname, + uint8_t *buf, uint32_t size) +{ + int nw, cnt = 0; + uint32_t written = 0; + + LOGV("opening file [%s]\n", fname); + int fd = open(fname, O_RDWR | O_CREAT); + if (fd < 0) { + LOGE("failed to create file [%s]: %s", fname, strerror(errno)); + return; + } + + LOGV("writing %d bytes to file [%s]\n", size, fname); + while (written < size) { + nw = ::write(fd, + buf + written, + size - written); + if (nw < 0) { + LOGE("failed to write to file [%s]: %s", + fname, strerror(errno)); + break; + } + written += nw; + cnt++; + } + LOGV("done writing %d bytes to file [%s] in %d passes\n", + size, fname, cnt); + ::close(fd); +} +#endif + +status_t CameraService::Client::autoFocus() +{ + LOGV("autoFocus (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->autoFocus(); +} + +status_t CameraService::Client::cancelAutoFocus() +{ + LOGV("cancelAutoFocus (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->cancelAutoFocus(); +} + +// take a picture - image is returned in callback +status_t CameraService::Client::takePicture() +{ + LOGV("takePicture (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + mHardware->enableMsgType(CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_COMPRESSED_IMAGE); + + return mHardware->takePicture(); +} + +// snapshot taken +void CameraService::Client::handleShutter( + image_rect_type *size // The width and height of yuv picture for + // registerBuffer. If this is NULL, use the picture + // size from parameters. +) +{ + // Play shutter sound. + if (mMediaPlayerClick.get() != NULL) { + // do not play shutter sound if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + mMediaPlayerClick->seekTo(0); + mMediaPlayerClick->start(); + } + } + + // Screen goes black after the buffer is unregistered. + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); + } + + sp<ICameraClient> c = mCameraClient; + if (c != NULL) { + c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); + } + mHardware->disableMsgType(CAMERA_MSG_SHUTTER); + + // It takes some time before yuvPicture callback to be called. + // Register the buffer for raw image here to reduce latency. + if (mSurface != 0 && !mUseOverlay) { + int w, h; + CameraParameters params(mHardware->getParameters()); + if (size == NULL) { + params.getPictureSize(&w, &h); + } else { + w = size->width; + h = size->height; + w &= ~1; + h &= ~1; + LOGV("Snapshot image width=%d, height=%d", w, h); + } + // FIXME: don't use hardcoded format constants here + ISurface::BufferHeap buffers(w, h, w, h, + HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0, + mHardware->getRawHeap()); + + mSurface->registerBuffers(buffers); + } +} + +// preview callback - frame buffer update +void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) +{ + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + +#if DEBUG_HEAP_LEAKS && 0 // debugging + if (gWeakHeap == NULL) { + if (gWeakHeap != heap) { + LOGV("SETTING PREVIEW HEAP"); + heap->trackMe(true, true); + gWeakHeap = heap; + } + } +#endif +#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE + { + if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) { + dump_to_file("/data/preview.yuv", + (uint8_t *)heap->base() + offset, size); + } + } +#endif + + if (!mUseOverlay) + { + Mutex::Autolock surfaceLock(mSurfaceLock); + if (mSurface != NULL) { + mSurface->postBuffer(offset); + } + } + + // local copy of the callback flags + int flags = mPreviewCallbackFlag; + + // is callback enabled? + if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) { + // If the enable bit is off, the copy-out and one-shot bits are ignored + LOGV("frame callback is diabled"); + return; + } + + // hold a strong pointer to the client + sp<ICameraClient> c = mCameraClient; + + // clear callback flags if no client or one-shot mode + if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { + LOGV("Disable preview callback"); + mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | + FRAME_CALLBACK_FLAG_COPY_OUT_MASK | + FRAME_CALLBACK_FLAG_ENABLE_MASK); + // TODO: Shouldn't we use this API for non-overlay hardware as well? + if (mUseOverlay) + mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } + + // Is the received frame copied out or not? + if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + LOGV("frame is copied"); + copyFrameAndPostCopiedFrame(c, heap, offset, size); + } else { + LOGV("frame is forwarded"); + c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem); + } +} + +// picture callback - postview image ready +void CameraService::Client::handlePostview(const sp<IMemory>& mem) +{ +#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only + { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + dump_to_file("/data/postview.yuv", + (uint8_t *)heap->base() + offset, size); + } +#endif + + sp<ICameraClient> c = mCameraClient; + if (c != NULL) { + c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem); + } + mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); +} + +// picture callback - raw image ready +void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) +{ + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); +#if DEBUG_HEAP_LEAKS && 0 // debugging + gWeakHeap = heap; // debugging +#endif + + //LOGV("handleRawPicture(%d, %d)", offset, size); +#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only + dump_to_file("/data/photo.yuv", + (uint8_t *)heap->base() + offset, size); +#endif + + // Put the YUV version of the snapshot in the preview display. + if (mSurface != 0 && !mUseOverlay) { + mSurface->postBuffer(offset); + } + + sp<ICameraClient> c = mCameraClient; + if (c != NULL) { + c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem); + } + mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE); +} + +// picture callback - compressed picture ready +void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) +{ +#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only + { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + dump_to_file("/data/photo.jpg", + (uint8_t *)heap->base() + offset, size); + } +#endif + + sp<ICameraClient> c = mCameraClient; + if (c != NULL) { + c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem); + } + mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); +} + +void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user) +{ + LOGV("notifyCallback(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + + switch (msgType) { + case CAMERA_MSG_SHUTTER: + // ext1 is the dimension of the yuv picture. + client->handleShutter((image_rect_type *)ext1); + break; + default: + sp<ICameraClient> c = client->mCameraClient; + if (c != NULL) { + c->notifyCallback(msgType, ext1, ext2); + } + break; + } + +#if DEBUG_CLIENT_REFERENCES + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user) +{ + LOGV("dataCallback(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + + sp<ICameraClient> c = client->mCameraClient; + if (dataPtr == NULL) { + LOGE("Null data returned in data callback"); + if (c != NULL) { + c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + c->dataCallback(msgType, NULL); + } + return; + } + + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: + client->handlePreviewData(dataPtr); + break; + case CAMERA_MSG_POSTVIEW_FRAME: + client->handlePostview(dataPtr); + break; + case CAMERA_MSG_RAW_IMAGE: + client->handleRawPicture(dataPtr); + break; + case CAMERA_MSG_COMPRESSED_IMAGE: + client->handleCompressedPicture(dataPtr); + break; + default: + if (c != NULL) { + c->dataCallback(msgType, dataPtr); + } + break; + } + +#if DEBUG_CLIENT_REFERENCES + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, + const sp<IMemory>& dataPtr, void* user) +{ + LOGV("dataCallbackTimestamp(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) { + return; + } + sp<ICameraClient> c = client->mCameraClient; + + if (dataPtr == NULL) { + LOGE("Null data returned in data with timestamp callback"); + if (c != NULL) { + c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + c->dataCallbackTimestamp(0, msgType, NULL); + } + return; + } + + if (c != NULL) { + c->dataCallbackTimestamp(timestamp, msgType, dataPtr); + } + +#if DEBUG_CLIENT_REFERENCES + if (client->getStrongCount() == 1) { + LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!"); + client->printRefs(); + } +#endif +} + +// set preview/capture parameters - key/value pairs +status_t CameraService::Client::setParameters(const String8& params) +{ + LOGV("setParameters(%s)", params.string()); + + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + CameraParameters p(params); + + return mHardware->setParameters(p); +} + +// get preview/capture parameters - key/value pairs +String8 CameraService::Client::getParameters() const +{ + Mutex::Autolock lock(mLock); + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return String8(); + } + + String8 params(mHardware->getParameters().flatten()); + LOGV("getParameters(%s)", params.string()); + return params; +} + +status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) +{ + LOGV("sendCommand (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + status_t result = checkPid(); + if (result != NO_ERROR) return result; + + if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { + // The orientation cannot be set during preview. + if (mHardware->previewEnabled()) { + return INVALID_OPERATION; + } + switch (arg1) { + case 0: + mOrientation = ISurface::BufferHeap::ROT_0; + break; + case 90: + mOrientation = ISurface::BufferHeap::ROT_90; + break; + case 180: + mOrientation = ISurface::BufferHeap::ROT_180; + break; + case 270: + mOrientation = ISurface::BufferHeap::ROT_270; + break; + default: + return BAD_VALUE; + } + return OK; + } + + if (mHardware == 0) { + LOGE("mHardware is NULL, returning."); + return INVALID_OPERATION; + } + + return mHardware->sendCommand(cmd, arg1, arg2); +} + +void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, size_t offset, size_t size) +{ + LOGV("copyFrameAndPostCopiedFrame"); + // It is necessary to copy out of pmem before sending this to + // the callback. For efficiency, reuse the same MemoryHeapBase + // provided it's big enough. Don't allocate the memory or + // perform the copy if there's no callback. + + // hold the preview lock while we grab a reference to the preview buffer + sp<MemoryHeapBase> previewBuffer; + { + Mutex::Autolock lock(mPreviewLock); + if (mPreviewBuffer == 0) { + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } else if (size > mPreviewBuffer->virtualSize()) { + mPreviewBuffer.clear(); + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } + if (mPreviewBuffer == 0) { + LOGE("failed to allocate space for preview buffer"); + return; + } + previewBuffer = mPreviewBuffer; + } + memcpy(previewBuffer->base(), + (uint8_t *)heap->base() + offset, size); + + sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size); + if (frame == 0) { + LOGE("failed to allocate space for frame callback"); + return; + } + client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame); +} + +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 CameraService::dump(int fd, const Vector<String16>& args) +{ + static const char* kDeadlockedString = "CameraService may be deadlocked\n"; + + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump CameraService from pid=%d, uid=%d\n", + getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - CameraService is probably deadlocked + if (!locked) { + String8 result(kDeadlockedString); + write(fd, result.string(), result.size()); + } + + if (mClient != 0) { + sp<Client> currentClient = mClient.promote(); + sprintf(buffer, "Client (%p) PID: %d\n", + currentClient->getCameraClient()->asBinder().get(), + currentClient->mClientPid); + result.append(buffer); + write(fd, result.string(), result.size()); + currentClient->mHardware->dump(fd, args); + } else { + result.append("No camera client yet.\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + + +status_t CameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // permission checks... + switch (code) { + case BnCameraService::CONNECT: + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (pid != self_pid) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.CAMERA"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't use the camera pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + break; + } + + status_t err = BnCameraService::onTransact(code, data, reply, flags); + +#if DEBUG_HEAP_LEAKS + LOGV("+++ onTransact err %d code %d", err, code); + + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + // the 'service' command interrogates this binder for its name, and then supplies it + // even for the debugging commands. that means we need to check for it here, using + // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to + // BnSurfaceComposer before falling through to this code). + + LOGV("+++ onTransact code %d", code); + + CHECK_INTERFACE(ICameraService, data, reply); + + switch(code) { + case 1000: + { + if (gWeakHeap != 0) { + sp<IMemoryHeap> h = gWeakHeap.promote(); + IMemoryHeap *p = gWeakHeap.unsafe_get(); + LOGV("CHECKING WEAK REFERENCE %p (%p)", h.get(), p); + if (h != 0) + h->printRefs(); + bool attempt_to_delete = data.readInt32() == 1; + if (attempt_to_delete) { + // NOT SAFE! + LOGV("DELETING WEAK REFERENCE %p (%p)", h.get(), p); + if (p) delete p; + } + return NO_ERROR; + } + } + break; + default: + break; + } + } +#endif // DEBUG_HEAP_LEAKS + + return err; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h new file mode 100644 index 0000000..bc49b1d --- /dev/null +++ b/services/camera/libcameraservice/CameraService.h @@ -0,0 +1,227 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** 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_CAMERASERVICE_H +#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H + +#include <camera/ICameraService.h> +#include <camera/CameraHardwareInterface.h> +#include <camera/Camera.h> + +namespace android { + +class MemoryHeapBase; +class MediaPlayer; + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// When enabled, this feature allows you to send an event to the CameraService +// so that you can cause all references to the heap object gWeakHeap, defined +// below, to be printed. You will also need to set DEBUG_REFS=1 and +// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to +// set gWeakHeap to the appropriate heap you want to track. + +#define DEBUG_HEAP_LEAKS 0 + +// ---------------------------------------------------------------------------- + +class CameraService : public BnCameraService +{ + class Client; + +public: + static void instantiate(); + + // ICameraService interface + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient); + + virtual status_t dump(int fd, const Vector<String16>& args); + + void removeClient(const sp<ICameraClient>& cameraClient); + + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + +private: + +// ---------------------------------------------------------------------------- + + class Client : public BnCamera { + + public: + virtual void disconnect(); + + // connect new client with existing camera remote + virtual status_t connect(const sp<ICameraClient>& client); + + // prevent other processes from using this ICamera interface + virtual status_t lock(); + + // allow other processes to use this ICamera interface + virtual status_t unlock(); + + // pass the buffered ISurface to the camera service + virtual status_t setPreviewDisplay(const sp<ISurface>& surface); + + // set the preview callback flag to affect how the received frames from + // preview are handled. + virtual void setPreviewCallbackFlag(int callback_flag); + + // start preview mode, must call setPreviewDisplay first + virtual status_t startPreview(); + + // stop preview mode + virtual void stopPreview(); + + // get preview state + virtual bool previewEnabled(); + + // start recording mode + virtual status_t startRecording(); + + // stop recording mode + virtual void stopRecording(); + + // get recording state + virtual bool recordingEnabled(); + + // release a recording frame + virtual void releaseRecordingFrame(const sp<IMemory>& mem); + + // auto focus + virtual status_t autoFocus(); + + // cancel auto focus + virtual status_t cancelAutoFocus(); + + // take a picture - returns an IMemory (ref-counted mmap) + virtual status_t takePicture(); + + // set preview/capture parameters - key/value pairs + virtual status_t setParameters(const String8& params); + + // get preview/capture parameters - key/value pairs + virtual String8 getParameters() const; + + // send command to camera driver + virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); + + // our client... + const sp<ICameraClient>& getCameraClient() const { return mCameraClient; } + + private: + friend class CameraService; + Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + pid_t clientPid); + Client(); + virtual ~Client(); + + status_t checkPid(); + + static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); + static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user); + static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, + const sp<IMemory>& dataPtr, void* user); + + static sp<Client> getClientFromCookie(void* user); + + void handlePreviewData(const sp<IMemory>&); + void handleShutter(image_rect_type *image); + void handlePostview(const sp<IMemory>&); + void handleRawPicture(const sp<IMemory>&); + void handleCompressedPicture(const sp<IMemory>&); + + void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, size_t offset, size_t size); + + // camera operation mode + enum camera_mode { + CAMERA_PREVIEW_MODE = 0, // frame automatically released + CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() + }; + status_t startCameraMode(camera_mode mode); + status_t startPreviewMode(); + status_t startRecordingMode(); + status_t setOverlay(); + status_t registerPreviewBuffers(); + + // Ensures atomicity among the public methods + mutable Mutex mLock; + + // mSurfaceLock synchronizes access to mSurface between + // setPreviewSurface() and postPreviewFrame(). Note that among + // the public methods, all accesses to mSurface are + // syncrhonized by mLock. However, postPreviewFrame() is called + // by the CameraHardwareInterface callback, and needs to + // access mSurface. It cannot hold mLock, however, because + // stopPreview() may be holding that lock while attempting + // to stop preview, and stopPreview itself will block waiting + // for a callback from CameraHardwareInterface. If this + // happens, it will cause a deadlock. + mutable Mutex mSurfaceLock; + mutable Condition mReady; + sp<CameraService> mCameraService; + sp<ISurface> mSurface; + int mPreviewCallbackFlag; + int mOrientation; + + sp<MediaPlayer> mMediaPlayerClick; + sp<MediaPlayer> mMediaPlayerBeep; + + // these are immutable once the object is created, + // they don't need to be protected by a lock + sp<ICameraClient> mCameraClient; + sp<CameraHardwareInterface> mHardware; + pid_t mClientPid; + bool mUseOverlay; + + sp<OverlayRef> mOverlayRef; + int mOverlayW; + int mOverlayH; + + mutable Mutex mPreviewLock; + sp<MemoryHeapBase> mPreviewBuffer; + }; + +// ---------------------------------------------------------------------------- + + CameraService(); + virtual ~CameraService(); + + // We use a count for number of clients (shoule only be 0 or 1). + volatile int32_t mUsers; + virtual void incUsers(); + virtual void decUsers(); + + mutable Mutex mServiceLock; + wp<Client> mClient; + +#if DEBUG_HEAP_LEAKS + wp<IMemoryHeap> gWeakHeap; +#endif +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h new file mode 100644 index 0000000..b6266fb --- /dev/null +++ b/services/camera/libcameraservice/CannedJpeg.h @@ -0,0 +1,734 @@ +const int kCannedJpegWidth = 320; +const int kCannedJpegHeight = 240; +const int kCannedJpegSize = 8733; + +const char kCannedJpeg[] = { + 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66, + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x1a, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x31, 0x01, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33, + 0x2e, 0x33, 0x36, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, + 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, + 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, 0x08, 0x0c, + 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d, + 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, + 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, + 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, + 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0, + 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, + 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, + 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, + 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, + 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, + 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, + 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, + 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, + 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, + 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, + 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xd2, 0xa3, 0x95, 0xbb, + 0x54, 0x84, 0xe0, 0x66, 0xa0, 0x27, 0x27, 0x35, 0xed, 0x9e, 0x50, 0x95, + 0x2c, 0x4b, 0xc6, 0x6a, 0x35, 0x1b, 0x8e, 0x2a, 0x70, 0x30, 0x28, 0x00, + 0xa8, 0xe5, 0x6e, 0x71, 0x52, 0x31, 0xda, 0x33, 0x50, 0x13, 0x93, 0x40, + 0x09, 0x52, 0xc6, 0xb8, 0x19, 0xf5, 0xa6, 0x2a, 0xee, 0x6c, 0x54, 0xd4, + 0x00, 0x54, 0x52, 0x36, 0x5b, 0x1e, 0x95, 0x23, 0xb6, 0xd5, 0xcd, 0x41, + 0x40, 0x05, 0x4c, 0x8b, 0xb5, 0x7d, 0xea, 0x34, 0x5d, 0xcd, 0xed, 0x53, + 0x50, 0x01, 0x50, 0xbb, 0x6e, 0x6f, 0x6a, 0x91, 0xdb, 0x6a, 0xfb, 0xd4, + 0x34, 0x00, 0x54, 0xe8, 0xbb, 0x57, 0x15, 0x1c, 0x6b, 0x96, 0xcf, 0xa5, + 0x4b, 0x40, 0x05, 0x42, 0xcd, 0xb9, 0xb3, 0x4f, 0x91, 0xb0, 0x31, 0xeb, + 0x51, 0x50, 0x02, 0x81, 0x93, 0x53, 0xa8, 0xda, 0x31, 0x51, 0xc4, 0xbc, + 0xe6, 0xa4, 0xa0, 0x00, 0x9c, 0x0a, 0x81, 0x8e, 0xe3, 0x9a, 0x92, 0x56, + 0xe3, 0x15, 0x15, 0x00, 0x28, 0x19, 0x38, 0xa9, 0xc0, 0xc0, 0xc5, 0x47, + 0x12, 0xf7, 0xa9, 0x28, 0x00, 0x27, 0x00, 0x9a, 0x80, 0x9c, 0x9c, 0xd3, + 0xe5, 0x6e, 0xd5, 0x1d, 0x00, 0x2a, 0x8d, 0xc7, 0x15, 0x3d, 0x32, 0x35, + 0xc0, 0xcf, 0xad, 0x3e, 0x80, 0x11, 0x8e, 0xd1, 0x9a, 0x82, 0x9f, 0x23, + 0x64, 0xe3, 0xd2, 0x99, 0x40, 0x0e, 0x45, 0xdc, 0xde, 0xd5, 0x35, 0x36, + 0x35, 0xc2, 0xfb, 0x9a, 0x75, 0x00, 0x35, 0xdb, 0x6a, 0xfb, 0xd4, 0x34, + 0xe9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, 0x00, 0x3e, 0x35, 0xcb, 0x7b, 0x0a, + 0x96, 0x91, 0x17, 0x6a, 0xd2, 0xd0, 0x03, 0x64, 0x6c, 0x2f, 0xb9, 0xa8, + 0x69, 0xce, 0xdb, 0x9a, 0x9b, 0xd6, 0x80, 0x1f, 0x12, 0xe4, 0xe7, 0xd2, + 0xa5, 0xa4, 0x51, 0xb4, 0x62, 0x97, 0xa5, 0x00, 0x67, 0xc9, 0xad, 0xd8, + 0x91, 0x81, 0x72, 0x9f, 0x9d, 0x47, 0xfd, 0xb3, 0x65, 0xff, 0x00, 0x3f, + 0x29, 0x5f, 0xa0, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, + 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, + 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd7, 0x3f, 0xb7, 0x87, 0x73, 0x6f, + 0x63, 0x33, 0xe0, 0x28, 0xf5, 0x9b, 0x11, 0xc9, 0xb9, 0x4c, 0xfd, 0x69, + 0xff, 0x00, 0xdb, 0x96, 0x1f, 0xf3, 0xf5, 0x1f, 0xe7, 0x5f, 0x7d, 0x7f, + 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, + 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, + 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, 0x02, 0x93, + 0x5b, 0xb1, 0x3c, 0x0b, 0x94, 0xc7, 0xd6, 0x99, 0xfd, 0xb3, 0x65, 0xff, + 0x00, 0x3f, 0x29, 0xf9, 0xd7, 0xe8, 0x07, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, + 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, + 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0xbd, 0xbc, 0x03, 0xd8, + 0xcc, 0xf8, 0x0e, 0x3d, 0x6a, 0xc1, 0x47, 0x37, 0x29, 0x9f, 0xad, 0x3b, + 0xfb, 0x72, 0xc3, 0xfe, 0x7e, 0xa3, 0xfc, 0xeb, 0xef, 0xaf, 0xf8, 0x74, + 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, + 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, + 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x19, 0x35, 0xbb, 0x26, + 0x3c, 0x5c, 0xa6, 0x3e, 0xb4, 0xdf, 0xed, 0x9b, 0x2f, 0xf9, 0xf9, 0x4a, + 0xfd, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, + 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, + 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x97, 0xb7, 0x80, 0x7b, 0x19, 0x9f, 0x01, + 0xa6, 0xb5, 0x60, 0xab, 0xff, 0x00, 0x1f, 0x51, 0xe7, 0xeb, 0x4e, 0xfe, + 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0xfb, 0xeb, 0xfe, + 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, + 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, + 0x3d, 0xbc, 0x03, 0xd8, 0xcc, 0xf8, 0x05, 0xf5, 0xab, 0x26, 0x6f, 0xf8, + 0xf9, 0x4c, 0x7d, 0x69, 0xbf, 0xdb, 0x36, 0x5f, 0xf3, 0xf2, 0x9f, 0x9d, + 0x7e, 0x80, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, + 0xff, 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, + 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x80, 0x7b, 0x19, 0x9f, + 0x02, 0x26, 0xb5, 0x60, 0xab, 0x8f, 0xb5, 0x47, 0xf9, 0xd2, 0xff, 0x00, + 0x6e, 0x58, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x7d, 0xf5, 0xff, 0x00, 0x0e, + 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, + 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, + 0xde, 0x01, 0xec, 0x66, 0x7c, 0x00, 0xda, 0xd5, 0x93, 0x1c, 0xfd, 0xa5, + 0x3f, 0x3a, 0x4f, 0xed, 0x8b, 0x2f, 0xf9, 0xf9, 0x4f, 0xce, 0xbf, 0x40, + 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, + 0xc0, 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, + 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa7, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, + 0x81, 0x57, 0x5a, 0xb0, 0x51, 0x8f, 0xb5, 0x47, 0xf9, 0xd1, 0xfd, 0xb9, + 0x61, 0xff, 0x00, 0x3f, 0x49, 0xf9, 0xd7, 0xdf, 0x5f, 0xf0, 0xe9, 0x6f, + 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, + 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd2, + 0xf6, 0xf0, 0x0f, 0x63, 0x33, 0xe0, 0x06, 0xd6, 0xac, 0x98, 0xe7, 0xed, + 0x29, 0xf9, 0xd2, 0x0d, 0x62, 0xcb, 0xfe, 0x7e, 0x53, 0xf3, 0xaf, 0xd0, + 0x0f, 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, + 0x1a, 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, + 0x00, 0xc0, 0x48, 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x51, + 0xad, 0xd8, 0x01, 0x8f, 0xb5, 0x47, 0xf9, 0xd0, 0x75, 0xcb, 0x0c, 0x7f, + 0xc7, 0xca, 0x7e, 0x75, 0xf7, 0xd7, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, + 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, + 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7b, 0x78, 0x77, 0x0f, 0x63, + 0x33, 0xf3, 0xfc, 0xeb, 0x36, 0x44, 0xff, 0x00, 0xc7, 0xca, 0x7e, 0x74, + 0xa3, 0x58, 0xb1, 0x24, 0x66, 0xe5, 0x31, 0xf5, 0xaf, 0xbf, 0xff, 0x00, + 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, + 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, + 0xff, 0x00, 0x01, 0x21, 0xa3, 0xdb, 0xc3, 0xb8, 0x7b, 0x19, 0x9f, 0x02, + 0xff, 0x00, 0x6d, 0xd8, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x07, 0x5c, 0xb1, + 0x03, 0x8b, 0x94, 0xcf, 0xd6, 0xbe, 0xfa, 0xff, 0x00, 0x87, 0x4b, 0x78, + 0x4b, 0xfe, 0x87, 0xdd, 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, + 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, + 0x0e, 0xe1, 0xec, 0x66, 0x7e, 0x7f, 0xff, 0x00, 0x6c, 0xd9, 0x7f, 0xcf, + 0xca, 0x7e, 0x74, 0xab, 0xac, 0x58, 0xe7, 0x9b, 0x94, 0xc7, 0xd6, 0xbe, + 0xff, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, + 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, + 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, 0x0e, 0xe1, 0xec, 0x66, 0x7c, + 0x0b, 0xfd, 0xb9, 0x61, 0xff, 0x00, 0x3f, 0x51, 0xfe, 0x74, 0x8d, 0xae, + 0x58, 0xed, 0x38, 0xb9, 0x4c, 0xfd, 0x6b, 0xef, 0xbf, 0xf8, 0x74, 0xb7, + 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, 0xd2, + 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, 0x68, + 0xf6, 0xf0, 0xee, 0x1e, 0xc6, 0x67, 0xe7, 0xff, 0x00, 0xf6, 0xc5, 0x97, + 0xfc, 0xfc, 0xa7, 0xe7, 0x4e, 0x4d, 0x62, 0xc7, 0x77, 0x37, 0x29, 0xf9, + 0xd7, 0xdf, 0xdf, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, + 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, + 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, + 0x81, 0x7f, 0xb7, 0x2c, 0x3f, 0xe7, 0xea, 0x3f, 0xce, 0x91, 0xf5, 0xcb, + 0x1c, 0x71, 0x72, 0x9f, 0x9d, 0x7d, 0xf7, 0xff, 0x00, 0x0e, 0x96, 0xf0, + 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, 0x3a, 0x5b, + 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, 0xde, 0x1d, + 0xc3, 0xd8, 0xcc, 0xfc, 0xff, 0x00, 0xfe, 0xd9, 0xb2, 0xff, 0x00, 0x9f, + 0x94, 0xfc, 0xe9, 0xd1, 0xeb, 0x36, 0x20, 0xe4, 0xdc, 0xa7, 0xe7, 0x5f, + 0x7f, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, + 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, + 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, + 0x05, 0xfe, 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0x6c, + 0x9a, 0xdd, 0x89, 0x18, 0x17, 0x29, 0xf9, 0xd7, 0xdf, 0x9f, 0xf0, 0xe9, + 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, + 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, + 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, 0xbc, 0xa8, 0xa2, 0x8a, 0xf3, + 0x0e, 0xf0, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, + 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, + 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0xa0, 0xbb, 0xbd, 0xb7, 0xb0, + 0x88, 0x49, 0x73, 0x3c, 0x56, 0xf1, 0x96, 0x0a, 0x1e, 0x57, 0x0a, 0x09, + 0x3d, 0x06, 0x4f, 0x7a, 0x9e, 0x95, 0xd3, 0x76, 0xea, 0x01, 0x45, 0x14, + 0x53, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x82, 0xda, 0xf6, 0xde, + 0xf0, 0xca, 0x2d, 0xe7, 0x8a, 0x73, 0x13, 0x98, 0xe4, 0xf2, 0xdc, 0x36, + 0xc6, 0x1d, 0x54, 0xe3, 0xa1, 0xf6, 0xa4, 0xda, 0x4e, 0xcc, 0x09, 0xe8, + 0xa2, 0x8a, 0x60, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, + 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, + 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x02, 0xb8, + 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45, + 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x18, 0x51, 0x45, 0x14, 0x0a, 0xe1, 0x45, + 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb9, 0xca, 0xfc, 0x4a, 0xf0, + 0x52, 0x78, 0xef, 0xc2, 0xb7, 0x1a, 0x76, 0xef, 0x2e, 0xe5, 0x4f, 0x9d, + 0x6c, 0xe4, 0xe0, 0x09, 0x00, 0x38, 0xcf, 0xb1, 0xc9, 0x1f, 0x8e, 0x7b, + 0x57, 0x3d, 0xf0, 0x5b, 0xc7, 0x53, 0x6b, 0xba, 0x6c, 0xda, 0x16, 0xaa, + 0x5a, 0x3d, 0x73, 0x4a, 0xfd, 0xd4, 0x8b, 0x2f, 0xdf, 0x91, 0x01, 0xc0, + 0x27, 0xdc, 0x1e, 0x0f, 0xe0, 0x7b, 0xd7, 0xa3, 0x5c, 0xdc, 0xc5, 0x67, + 0x04, 0x93, 0xcf, 0x2a, 0x43, 0x0c, 0x60, 0xb3, 0xc9, 0x23, 0x05, 0x55, + 0x1e, 0xa4, 0x9e, 0x95, 0xf3, 0x47, 0xc4, 0x8f, 0x1f, 0xe9, 0x36, 0xdf, + 0x10, 0xed, 0x3c, 0x41, 0xe1, 0x39, 0x99, 0xaf, 0xa1, 0xe2, 0xea, 0x42, + 0x98, 0x82, 0x72, 0x38, 0xe3, 0x90, 0x4e, 0x46, 0x41, 0xe9, 0x9c, 0x0c, + 0x7a, 0xd7, 0xc4, 0x67, 0x98, 0x9a, 0x59, 0x3e, 0x26, 0x9e, 0x64, 0xa6, + 0x93, 0x7e, 0xec, 0xe3, 0x7d, 0x65, 0x1e, 0xe9, 0x77, 0x8b, 0xd7, 0xd3, + 0x4b, 0x99, 0x4d, 0xa8, 0xbe, 0x63, 0xe9, 0xca, 0x2b, 0xe4, 0x3d, 0x73, + 0xe3, 0x3f, 0x8b, 0xb5, 0xc6, 0x6d, 0xfa, 0xb4, 0x96, 0x71, 0x9e, 0x91, + 0x59, 0x0f, 0x28, 0x0f, 0xc4, 0x7c, 0xdf, 0x99, 0xae, 0x56, 0xe7, 0x5a, + 0xd4, 0x6f, 0x18, 0xb5, 0xc5, 0xfd, 0xd4, 0xec, 0x7b, 0xc9, 0x33, 0x31, + 0xfd, 0x4d, 0x78, 0x75, 0xf8, 0xfb, 0x0b, 0x07, 0x6a, 0x14, 0x65, 0x25, + 0xe6, 0xd2, 0xff, 0x00, 0x32, 0x1d, 0x75, 0xd1, 0x1f, 0x73, 0x51, 0x5f, + 0x0b, 0xdb, 0xea, 0xf7, 0xf6, 0xad, 0xba, 0x0b, 0xdb, 0x88, 0x58, 0x77, + 0x8e, 0x56, 0x53, 0xfa, 0x1a, 0xe9, 0xf4, 0x5f, 0x8b, 0xfe, 0x2e, 0xd0, + 0xd9, 0x7c, 0xad, 0x66, 0x7b, 0x84, 0x1f, 0xf2, 0xce, 0xec, 0xf9, 0xc0, + 0xff, 0x00, 0xdf, 0x59, 0x23, 0xf0, 0x34, 0xa8, 0x71, 0xf6, 0x1a, 0x4e, + 0xd5, 0xa8, 0x4a, 0x2b, 0xc9, 0xa7, 0xfe, 0x40, 0xab, 0xae, 0xa8, 0xfa, + 0x13, 0xe2, 0xff, 0x00, 0x8f, 0xcf, 0x82, 0xfc, 0x3e, 0x21, 0xb3, 0x6d, + 0xda, 0xcd, 0xfe, 0x62, 0xb5, 0x45, 0xe5, 0x97, 0xb1, 0x7c, 0x7b, 0x67, + 0x8f, 0x72, 0x3d, 0xea, 0x5f, 0x84, 0x7e, 0x05, 0x6f, 0x04, 0x78, 0x60, + 0x2d, 0xd1, 0x2d, 0xa9, 0xde, 0xb0, 0x9e, 0xe8, 0x93, 0x9d, 0xad, 0x8e, + 0x17, 0xf0, 0x1d, 0x4f, 0xa9, 0x35, 0xe2, 0x5e, 0x13, 0xf8, 0x89, 0x61, + 0xac, 0xfc, 0x49, 0x8f, 0xc4, 0x3e, 0x30, 0x76, 0xcc, 0x68, 0x16, 0xd8, + 0x43, 0x19, 0x68, 0x61, 0x61, 0xd0, 0x91, 0x92, 0x40, 0x1c, 0x9e, 0x33, + 0xc9, 0xcd, 0x7d, 0x3b, 0x63, 0x7f, 0x6d, 0xaa, 0x5a, 0x45, 0x75, 0x69, + 0x3c, 0x77, 0x36, 0xd2, 0x8d, 0xc9, 0x2c, 0x4c, 0x19, 0x58, 0x7b, 0x11, + 0x5e, 0xde, 0x4d, 0x8b, 0xa3, 0x9d, 0xe3, 0x2a, 0x66, 0x1c, 0xe9, 0xf2, + 0x5e, 0x30, 0x8f, 0x58, 0xae, 0xb2, 0x6b, 0xbc, 0xbf, 0x05, 0xa1, 0x50, + 0x6a, 0x6f, 0x98, 0xb1, 0x45, 0x14, 0x57, 0xdc, 0x9b, 0x5c, 0x28, 0xa2, + 0x8a, 0x02, 0xe1, 0x45, 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb8, + 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45, + 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x0b, 0x8d, 0xcd, 0x19, 0xa6, 0xe4, 0x51, + 0x91, 0x55, 0x62, 0x47, 0x66, 0x8c, 0xd3, 0x72, 0x28, 0xc8, 0xa2, 0xc0, + 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x01, 0xd9, 0xa3, 0x34, + 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, 0xcd, 0x19, 0xa6, 0xe4, 0x52, 0xe4, + 0x51, 0x60, 0xb8, 0xb9, 0xa3, 0x34, 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, + 0xdd, 0x46, 0x69, 0xb9, 0x14, 0x64, 0x51, 0x60, 0x1d, 0x9a, 0xa7, 0xac, + 0x6b, 0x16, 0x9a, 0x0e, 0x9b, 0x71, 0xa8, 0x5f, 0x4c, 0x20, 0xb5, 0x81, + 0x37, 0xbb, 0x9e, 0xc3, 0xd0, 0x7a, 0x93, 0xd0, 0x0a, 0xb5, 0x91, 0x5f, + 0x39, 0xfe, 0xd1, 0x1e, 0x37, 0x7d, 0x4b, 0x5a, 0x4f, 0x0f, 0x5b, 0x48, + 0x45, 0xa5, 0x96, 0x1e, 0x70, 0xa7, 0xef, 0xca, 0x46, 0x40, 0x3f, 0xee, + 0x83, 0xf9, 0x93, 0xe9, 0x5e, 0x06, 0x79, 0x9a, 0xc3, 0x27, 0xc1, 0x4b, + 0x12, 0xd5, 0xe5, 0xb4, 0x57, 0x76, 0xff, 0x00, 0xab, 0xbf, 0x24, 0x44, + 0xe5, 0xca, 0xae, 0x72, 0xbf, 0x12, 0xbe, 0x2a, 0xea, 0x3e, 0x3e, 0xbd, + 0x78, 0xd5, 0x9e, 0xd3, 0x48, 0x46, 0xfd, 0xd5, 0xa2, 0x9f, 0xbd, 0xe8, + 0xcf, 0xea, 0x7f, 0x41, 0xdb, 0xd4, 0xc3, 0xe0, 0x5f, 0x85, 0x1a, 0xd7, + 0x8f, 0xed, 0xe6, 0xb9, 0xb1, 0xf2, 0x2d, 0xed, 0x22, 0x6d, 0x86, 0x7b, + 0x96, 0x21, 0x59, 0xb1, 0x9c, 0x0c, 0x02, 0x4f, 0x51, 0xf9, 0xd7, 0x19, + 0x5e, 0xcd, 0xf0, 0x73, 0xe3, 0x16, 0x97, 0xe1, 0x0d, 0x06, 0x4d, 0x23, + 0x57, 0x49, 0x63, 0x44, 0x95, 0xa5, 0x86, 0x78, 0x53, 0x78, 0x21, 0xba, + 0xab, 0x0e, 0xb9, 0xcf, 0x7f, 0x7f, 0x6a, 0xfc, 0x1b, 0x2e, 0xa9, 0x87, + 0xcd, 0xb3, 0x2f, 0x69, 0x9c, 0xd5, 0x6a, 0x2e, 0xfa, 0xde, 0xda, 0xf4, + 0x57, 0xe8, 0xbf, 0xe1, 0x8e, 0x48, 0xda, 0x52, 0xf7, 0x8f, 0x30, 0xf1, + 0x57, 0x85, 0x75, 0x0f, 0x06, 0xeb, 0x12, 0x69, 0xba, 0x94, 0x42, 0x3b, + 0x84, 0x01, 0x83, 0x21, 0xca, 0xba, 0x9e, 0x8c, 0xa7, 0xb8, 0xac, 0x8a, + 0xed, 0x3e, 0x2c, 0xf8, 0xee, 0x1f, 0x1f, 0xf8, 0x9c, 0x5e, 0xda, 0xc2, + 0xf0, 0xda, 0x41, 0x08, 0x82, 0x2f, 0x33, 0x01, 0xd8, 0x02, 0x49, 0x63, + 0xe9, 0xc9, 0x3c, 0x57, 0x17, 0x5e, 0x26, 0x3e, 0x9e, 0x1e, 0x96, 0x2a, + 0xa4, 0x30, 0xb2, 0xe6, 0xa6, 0x9b, 0xb3, 0xee, 0x88, 0x76, 0xbe, 0x81, + 0x5a, 0x1a, 0x06, 0x83, 0x7b, 0xe2, 0x7d, 0x5e, 0xdf, 0x4d, 0xd3, 0xe2, + 0xf3, 0xae, 0xa7, 0x38, 0x55, 0xce, 0x00, 0x00, 0x64, 0x92, 0x7b, 0x00, + 0x39, 0xac, 0xfa, 0xea, 0x3e, 0x1b, 0x78, 0xc1, 0x7c, 0x0d, 0xe2, 0xcb, + 0x5d, 0x52, 0x58, 0x4c, 0xf6, 0xe1, 0x5a, 0x39, 0x51, 0x3e, 0xf6, 0xd6, + 0x18, 0x24, 0x7b, 0x8e, 0x0d, 0x67, 0x83, 0x85, 0x1a, 0x98, 0x8a, 0x70, + 0xc4, 0x4b, 0x96, 0x0d, 0xae, 0x67, 0xd9, 0x5f, 0x50, 0x56, 0xbe, 0xa6, + 0x97, 0x8d, 0x7e, 0x0e, 0xeb, 0xde, 0x06, 0xd3, 0x17, 0x50, 0xbb, 0x36, + 0xf7, 0x56, 0x99, 0x0b, 0x24, 0x96, 0xae, 0x4f, 0x96, 0x4f, 0x4d, 0xc0, + 0x81, 0xc1, 0x3c, 0x66, 0xa9, 0xfc, 0x3e, 0xf8, 0x93, 0xaa, 0x78, 0x03, + 0x50, 0x0f, 0x6c, 0xe6, 0x7b, 0x07, 0x6f, 0xdf, 0xd9, 0x3b, 0x7c, 0x8e, + 0x3d, 0x47, 0xa3, 0x7b, 0xfe, 0x79, 0xaf, 0x45, 0xf8, 0xad, 0xf1, 0xb3, + 0x47, 0xf1, 0x27, 0x85, 0x26, 0xd2, 0x34, 0x84, 0x9a, 0x67, 0xbb, 0x2b, + 0xe6, 0xcb, 0x34, 0x7b, 0x04, 0x6a, 0x18, 0x36, 0x07, 0xa9, 0xc8, 0x1e, + 0xd5, 0xe1, 0x95, 0xf4, 0x39, 0xab, 0xc2, 0x65, 0x79, 0x84, 0x67, 0x93, + 0x55, 0x6d, 0x24, 0x9d, 0xd3, 0xbd, 0x9f, 0x55, 0x7e, 0xaa, 0xd6, 0xbe, + 0xfb, 0xd8, 0xb9, 0x5a, 0x32, 0xf7, 0x59, 0xf6, 0xef, 0x86, 0xbc, 0x49, + 0x63, 0xe2, 0xbd, 0x1a, 0xdf, 0x53, 0xd3, 0xe5, 0xf3, 0x2d, 0xe6, 0x1d, + 0xfe, 0xf2, 0x1e, 0xea, 0xc3, 0xb1, 0x15, 0xa9, 0x9a, 0xf9, 0x7b, 0xe0, + 0x27, 0x8d, 0xe4, 0xf0, 0xef, 0x8a, 0x53, 0x4a, 0x9e, 0x43, 0xfd, 0x9f, + 0xa9, 0x30, 0x8f, 0x69, 0x3c, 0x24, 0xdf, 0xc0, 0xc3, 0xeb, 0xf7, 0x7f, + 0x11, 0xe9, 0x5f, 0x4f, 0xe4, 0x57, 0xee, 0x3c, 0x3f, 0x9b, 0xc7, 0x39, + 0xc1, 0x2a, 0xed, 0x5a, 0x6b, 0x49, 0x2f, 0x3f, 0xf2, 0x7b, 0xfe, 0x1d, + 0x0e, 0xb8, 0x4f, 0x99, 0x5c, 0x76, 0x4d, 0x19, 0xa6, 0xe4, 0x51, 0x91, + 0x5f, 0x4b, 0x62, 0xc7, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, + 0x2e, 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x0b, 0x8e, 0xcd, + 0x19, 0xa6, 0xe4, 0x51, 0x91, 0x45, 0x80, 0x76, 0x68, 0xcd, 0x37, 0x34, + 0x64, 0x51, 0x60, 0xb8, 0xec, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, + 0x07, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, 0x06, 0x6e, 0xa3, + 0x75, 0x37, 0x34, 0x66, 0xae, 0xc4, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, + 0x46, 0x68, 0xb0, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, 0x46, 0x68, 0xb0, + 0x5c, 0x76, 0xea, 0x37, 0x53, 0x72, 0x28, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, + 0x46, 0xea, 0x6e, 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0x6e, + 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0xc4, 0xf1, 0x57, 0x8c, + 0x34, 0xaf, 0x06, 0x69, 0xff, 0x00, 0x6b, 0xd5, 0x2e, 0x44, 0x28, 0xc7, + 0x08, 0x8a, 0x37, 0x3c, 0x87, 0xd1, 0x47, 0x7f, 0xe5, 0x5c, 0x0d, 0x9f, + 0xed, 0x1f, 0xe1, 0xcb, 0x8b, 0xc1, 0x14, 0xd6, 0x97, 0xf6, 0xb0, 0x93, + 0x81, 0x3b, 0xa2, 0xb0, 0x1e, 0xe4, 0x06, 0x27, 0xf2, 0xcd, 0x78, 0xf8, + 0xac, 0xdf, 0x2f, 0xc0, 0xd4, 0x54, 0x71, 0x35, 0xa3, 0x19, 0x3e, 0x8d, + 0xfe, 0x7d, 0xbe, 0x64, 0xb9, 0x25, 0xb9, 0xeb, 0x05, 0xf6, 0x82, 0x4f, + 0x41, 0x5f, 0x10, 0xeb, 0x7a, 0x93, 0xeb, 0x3a, 0xcd, 0xf5, 0xfc, 0x84, + 0x97, 0xb9, 0x9d, 0xe6, 0x39, 0xff, 0x00, 0x69, 0x89, 0xfe, 0xb5, 0xf6, + 0xad, 0x8e, 0xa1, 0x6b, 0xab, 0x58, 0xc5, 0x75, 0x69, 0x34, 0x77, 0x36, + 0xb3, 0x2e, 0xe4, 0x91, 0x0e, 0x55, 0x85, 0x78, 0x5f, 0xfc, 0x2d, 0x5f, + 0x87, 0x3f, 0xf4, 0x25, 0x27, 0xfe, 0x00, 0xdb, 0xff, 0x00, 0x8d, 0x7c, + 0x67, 0x18, 0xe1, 0xa8, 0x63, 0x61, 0x87, 0x55, 0x31, 0x31, 0xa7, 0x1f, + 0x79, 0xab, 0xdd, 0xf3, 0x7c, 0x3a, 0xab, 0x76, 0xfd, 0x4c, 0xea, 0x59, + 0xdb, 0x53, 0xc4, 0x68, 0xaf, 0x6e, 0xff, 0x00, 0x85, 0xab, 0xf0, 0xe7, + 0xfe, 0x84, 0xa4, 0xff, 0x00, 0xc0, 0x1b, 0x7f, 0xf1, 0xa3, 0xfe, 0x16, + 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00, + 0xc6, 0xbf, 0x33, 0xfe, 0xc5, 0xc0, 0xff, 0x00, 0xd0, 0x7c, 0x3e, 0xe9, + 0x7f, 0x91, 0x8f, 0x2a, 0xee, 0x78, 0x8d, 0x15, 0xed, 0xdf, 0xf0, 0xb5, + 0x7e, 0x1c, 0xff, 0x00, 0xd0, 0x94, 0x9f, 0xf8, 0x03, 0x6f, 0xfe, 0x34, + 0x7f, 0xc2, 0xd5, 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, + 0xbf, 0xf8, 0xd1, 0xfd, 0x8b, 0x81, 0xff, 0x00, 0xa0, 0xf8, 0x7d, 0xd2, + 0xff, 0x00, 0x20, 0xe5, 0x5d, 0xcf, 0x11, 0xa2, 0xbd, 0xbb, 0xfe, 0x16, + 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00, + 0xc6, 0x8f, 0xf8, 0x5a, 0xbf, 0x0e, 0x7f, 0xe8, 0x4a, 0x4f, 0xfc, 0x01, + 0xb7, 0xff, 0x00, 0x1a, 0x3f, 0xb1, 0x70, 0x3f, 0xf4, 0x1f, 0x0f, 0xba, + 0x5f, 0xe4, 0x1c, 0xab, 0xb9, 0xe2, 0x34, 0x57, 0xb7, 0x7f, 0xc2, 0xd5, + 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd1, + 0xff, 0x00, 0x0b, 0x57, 0xe1, 0xcf, 0xfd, 0x09, 0x49, 0xff, 0x00, 0x80, + 0x36, 0xff, 0x00, 0xe3, 0x47, 0xf6, 0x2e, 0x07, 0xfe, 0x83, 0xe1, 0xf7, + 0x4b, 0xfc, 0x83, 0x95, 0x77, 0x3c, 0x52, 0x09, 0xde, 0xda, 0x78, 0xe6, + 0x89, 0x8a, 0x49, 0x1b, 0x07, 0x56, 0x1d, 0x41, 0x07, 0x20, 0xd7, 0xdb, + 0xfa, 0x5d, 0xf0, 0xd4, 0x74, 0xdb, 0x4b, 0xb0, 0x30, 0x27, 0x85, 0x25, + 0x03, 0xfd, 0xe5, 0x07, 0xfa, 0xd7, 0x85, 0xff, 0x00, 0xc2, 0xd5, 0xf8, + 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd7, 0xb6, + 0x69, 0x1a, 0x95, 0xa5, 0xc7, 0x87, 0xec, 0xaf, 0xe1, 0x55, 0xb3, 0xb1, + 0x7b, 0x54, 0x99, 0x11, 0xf0, 0x82, 0x28, 0xca, 0x02, 0x01, 0xec, 0x30, + 0x3f, 0x0e, 0x2b, 0xf4, 0x7e, 0x0e, 0xc2, 0xd0, 0xc1, 0xce, 0xbc, 0x69, + 0x62, 0x63, 0x51, 0x34, 0x9b, 0x4a, 0xfa, 0x5a, 0xfa, 0xeb, 0xea, 0x6d, + 0x4e, 0xca, 0xfa, 0x9a, 0x5b, 0xa8, 0xdd, 0x5e, 0x57, 0xab, 0xfe, 0xd1, + 0x3e, 0x1b, 0xd3, 0xaf, 0x1a, 0x0b, 0x68, 0x6e, 0xf5, 0x15, 0x53, 0x83, + 0x34, 0x28, 0xaa, 0x87, 0xe9, 0xb8, 0x82, 0x7f, 0x2a, 0xeb, 0xbc, 0x1b, + 0xf1, 0x07, 0x45, 0xf1, 0xcd, 0xbb, 0xbe, 0x99, 0x70, 0x7c, 0xe8, 0xc6, + 0x64, 0xb6, 0x98, 0x6d, 0x91, 0x07, 0xa9, 0x1d, 0xc7, 0xb8, 0xc8, 0xaf, + 0xb7, 0xc3, 0xe7, 0x19, 0x76, 0x2a, 0xb7, 0xd5, 0xe8, 0x56, 0x8c, 0xa7, + 0xd9, 0x3f, 0xcb, 0xbf, 0xc8, 0xd1, 0x49, 0x3d, 0x2e, 0x74, 0xdb, 0xa8, + 0xcd, 0x37, 0x34, 0x64, 0x57, 0xb2, 0x55, 0xc7, 0x6e, 0xa3, 0x75, 0x37, + 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, + 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, + 0xa3, 0x75, 0x37, 0x34, 0x64, 0x51, 0x60, 0xb8, 0xed, 0xd4, 0x6e, 0xa6, + 0xe4, 0x51, 0x9a, 0x2c, 0x17, 0x1b, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, + 0x55, 0x88, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, 0xd4, 0x58, 0x2e, + 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, 0xc9, 0x32, 0x28, + 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, 0x1e, 0xea, + 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8c, 0xd3, 0xb0, + 0x5c, 0xf9, 0x7b, 0xe3, 0xb6, 0xaf, 0x3e, 0xa5, 0xf1, 0x0e, 0xf2, 0xde, + 0x47, 0x26, 0x1b, 0x24, 0x48, 0x62, 0x4e, 0xc0, 0x15, 0x0c, 0x4f, 0xe2, + 0x58, 0xfe, 0x95, 0xe7, 0x95, 0xda, 0x7c, 0x64, 0xff, 0x00, 0x92, 0x97, + 0xad, 0xff, 0x00, 0xbf, 0x1f, 0xfe, 0x8a, 0x4a, 0xe2, 0xeb, 0xf9, 0x47, + 0x3a, 0x9c, 0xaa, 0x66, 0x78, 0x99, 0x49, 0xdd, 0xf3, 0xcb, 0xf0, 0x6d, + 0x23, 0x92, 0x5b, 0x9e, 0xf7, 0xfb, 0x34, 0xeb, 0x13, 0xcd, 0x67, 0xac, + 0xe9, 0xb2, 0x39, 0x6b, 0x78, 0x1a, 0x39, 0xa2, 0x04, 0xfd, 0xd2, 0xdb, + 0x83, 0x0f, 0xc7, 0x68, 0xfd, 0x6b, 0xc1, 0x2b, 0xda, 0xff, 0x00, 0x66, + 0x73, 0x8b, 0xed, 0x7f, 0xfe, 0xb9, 0xc3, 0xfc, 0xde, 0xbc, 0x52, 0xbd, + 0x8c, 0xd2, 0x72, 0x9e, 0x4b, 0x97, 0x39, 0x3b, 0xdb, 0xda, 0xaf, 0x92, + 0x92, 0xb1, 0x4f, 0xe1, 0x41, 0x45, 0x14, 0x57, 0xc6, 0x90, 0x14, 0x51, + 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0xef, + 0x7f, 0x13, 0xf5, 0x89, 0xf4, 0xef, 0x82, 0xbe, 0x19, 0xb6, 0x81, 0xca, + 0x0b, 0xc8, 0x2d, 0x62, 0x94, 0x83, 0xd5, 0x04, 0x3b, 0x88, 0xfc, 0x48, + 0x15, 0xe0, 0x95, 0xed, 0x7f, 0x17, 0x0f, 0xfc, 0x5a, 0x5f, 0x05, 0xff, + 0x00, 0xd7, 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x96, 0x47, 0x39, 0x43, 0x03, + 0x98, 0x4a, 0x2e, 0xcf, 0x91, 0x7e, 0x32, 0xb3, 0xfc, 0x0a, 0x8e, 0xcc, + 0xf1, 0x4a, 0xe9, 0xbe, 0x1a, 0x6a, 0xf3, 0xe8, 0xbe, 0x3a, 0xd1, 0x67, + 0x81, 0xca, 0x99, 0x2e, 0x52, 0x07, 0x00, 0xfd, 0xe4, 0x76, 0x0a, 0xc0, + 0xfe, 0x07, 0xf4, 0xae, 0x66, 0xb6, 0x3c, 0x1b, 0xff, 0x00, 0x23, 0x7e, + 0x87, 0xff, 0x00, 0x5f, 0xd0, 0x7f, 0xe8, 0xc5, 0xaf, 0x9b, 0xc0, 0xce, + 0x54, 0xf1, 0x54, 0xa7, 0x07, 0x66, 0xa4, 0xbf, 0x31, 0x2d, 0xcf, 0xb4, + 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xaf, 0xeb, 0x9b, 0x1d, 0x57, 0x24, + 0xcd, 0x19, 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, + 0x1e, 0xea, 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, + 0xd4, 0x58, 0x2e, 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, + 0xc9, 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xa2, 0xc3, 0xb8, 0xdd, 0xd4, + 0x9b, 0xa9, 0xbb, 0xa8, 0xdd, 0x5a, 0x19, 0xdc, 0x7e, 0xea, 0x4d, 0xd4, + 0xdd, 0xd4, 0x6e, 0xa0, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, + 0xa0, 0x77, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, 0xa4, 0x2b, 0x8f, + 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0xc2, 0xe3, 0xb7, 0x52, 0xee, + 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb9, 0xf2, 0x9f, 0xc6, 0x3f, 0xf9, 0x29, + 0x5a, 0xdf, 0xfb, 0xf1, 0xff, 0x00, 0xe8, 0xb4, 0xae, 0x32, 0xbb, 0x2f, + 0x8c, 0x5c, 0xfc, 0x49, 0xd6, 0xff, 0x00, 0xdf, 0x8f, 0xff, 0x00, 0x45, + 0xa5, 0x71, 0xb5, 0xfc, 0x99, 0x9c, 0x7f, 0xc8, 0xcb, 0x13, 0xfe, 0x39, + 0xff, 0x00, 0xe9, 0x4c, 0xc1, 0xee, 0x7b, 0x57, 0xec, 0xd2, 0x71, 0x7d, + 0xaf, 0x7f, 0xd7, 0x38, 0x7f, 0x9b, 0xd7, 0x8a, 0xd7, 0xb4, 0x7e, 0xcd, + 0x67, 0x17, 0xda, 0xf7, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, 0xbd, 0x7b, + 0x19, 0x97, 0xfc, 0x89, 0x32, 0xef, 0xfb, 0x8b, 0xff, 0x00, 0xa5, 0x21, + 0xbd, 0x90, 0x51, 0x45, 0x15, 0xf2, 0x02, 0x0a, 0x28, 0xa2, 0x80, 0x0a, + 0x28, 0xa2, 0x80, 0x0a, 0x28, 0xa2, 0x80, 0x0a, 0xf6, 0xaf, 0x8b, 0x47, + 0x3f, 0x09, 0x7c, 0x17, 0xff, 0x00, 0x5c, 0xe0, 0xff, 0x00, 0xd1, 0x15, + 0xe2, 0xb5, 0xed, 0x1f, 0x16, 0x4f, 0xfc, 0x5a, 0x6f, 0x06, 0x7f, 0xd7, + 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x7e, 0x4d, 0xff, 0x00, 0x22, 0xfc, 0xc3, + 0xfc, 0x11, 0xff, 0x00, 0xd2, 0x90, 0xd6, 0xcc, 0xf1, 0x7a, 0xd8, 0xf0, + 0x67, 0xfc, 0x8e, 0x1a, 0x17, 0xfd, 0x7f, 0xc1, 0xff, 0x00, 0xa3, 0x16, + 0xb1, 0xeb, 0x63, 0xc1, 0xdf, 0xf2, 0x37, 0x68, 0x7f, 0xf5, 0xfd, 0x07, + 0xfe, 0x8c, 0x5a, 0xf9, 0xbc, 0x27, 0xfb, 0xcd, 0x3f, 0xf1, 0x2f, 0xcc, + 0x48, 0xfb, 0x2b, 0x75, 0x1b, 0xa9, 0x9b, 0xa8, 0xdd, 0x5f, 0xd8, 0x16, + 0x37, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb8, 0xed, + 0xd4, 0xbb, 0xa9, 0x9b, 0xa8, 0xdd, 0x4c, 0x2e, 0x3f, 0x75, 0x26, 0xea, + 0x6e, 0xea, 0x37, 0x52, 0x0b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, + 0xd4, 0xec, 0x3b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0x0a, + 0xe4, 0x74, 0x53, 0x33, 0x4b, 0x93, 0x57, 0x63, 0x3b, 0x8e, 0xa2, 0x9b, + 0x9a, 0x4c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa3, 0x34, 0x58, 0x2e, + 0x3f, 0x34, 0x53, 0x33, 0x4b, 0x93, 0x45, 0x82, 0xe3, 0xa8, 0xcd, 0x33, + 0x34, 0x66, 0x8b, 0x05, 0xc7, 0xd1, 0x4d, 0xc9, 0xa3, 0x26, 0x8b, 0x05, + 0xcf, 0x96, 0x3e, 0x30, 0x7f, 0xc9, 0x48, 0xd6, 0xbf, 0xdf, 0x8f, 0xff, + 0x00, 0x45, 0xa5, 0x71, 0xb5, 0xdd, 0xfc, 0x6c, 0xd3, 0xa6, 0xb1, 0xf8, + 0x85, 0x7f, 0x2c, 0x8a, 0x44, 0x77, 0x4b, 0x1c, 0xd1, 0xb7, 0x66, 0x1b, + 0x02, 0x9f, 0xd5, 0x4d, 0x70, 0x95, 0xfc, 0x97, 0x9d, 0x42, 0x50, 0xcc, + 0xf1, 0x31, 0x92, 0xb7, 0xbf, 0x2f, 0xfd, 0x29, 0x90, 0x7b, 0x3f, 0xec, + 0xdb, 0xff, 0x00, 0x1f, 0xba, 0xef, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, + 0xc5, 0x7b, 0x87, 0xec, 0xe3, 0xa7, 0x4d, 0x1c, 0x3a, 0xd5, 0xf3, 0x29, + 0x58, 0x24, 0x31, 0xc2, 0x8d, 0xfd, 0xe2, 0x37, 0x16, 0xfc, 0xb2, 0x3f, + 0x3a, 0xf0, 0xfa, 0xf6, 0x73, 0x58, 0x4a, 0x19, 0x26, 0x5b, 0xcc, 0xad, + 0x7f, 0x6a, 0xff, 0x00, 0xf2, 0x64, 0x01, 0x45, 0x14, 0x57, 0xc6, 0x00, + 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, + 0x57, 0xb3, 0xfc, 0x57, 0xff, 0x00, 0x92, 0x51, 0xe0, 0xdf, 0xfa, 0xe7, + 0x07, 0xfe, 0x88, 0xaf, 0x18, 0xaf, 0x70, 0xf8, 0x9b, 0xa7, 0x4d, 0x79, + 0xf0, 0x77, 0xc3, 0x37, 0x11, 0x29, 0x74, 0xb5, 0x8a, 0xd9, 0xe4, 0xc7, + 0x65, 0x30, 0xed, 0xcf, 0xe6, 0x40, 0xfc, 0x6b, 0xec, 0xf2, 0x38, 0x4a, + 0x78, 0x0c, 0xc1, 0x45, 0x5f, 0xdc, 0x5f, 0x84, 0xae, 0xc0, 0xf0, 0xfa, + 0xd8, 0xf0, 0x6f, 0xfc, 0x8d, 0xfa, 0x1f, 0xfd, 0x7f, 0x41, 0xff, 0x00, + 0xa3, 0x16, 0xb1, 0xeb, 0xa2, 0xf8, 0x79, 0xa7, 0x4d, 0xaa, 0x78, 0xdf, + 0x45, 0x86, 0x15, 0x2c, 0xcb, 0x75, 0x1c, 0xad, 0x8e, 0xca, 0x8c, 0x18, + 0x9f, 0xc8, 0x57, 0xcd, 0x60, 0x61, 0x29, 0xe2, 0xe9, 0x46, 0x2a, 0xed, + 0xca, 0x3f, 0x9a, 0x03, 0xeb, 0x9c, 0xd1, 0x4c, 0xcd, 0x2e, 0x4d, 0x7f, + 0x60, 0x58, 0xbb, 0x8e, 0xa2, 0x99, 0x9a, 0x33, 0x45, 0x82, 0xe3, 0xe8, + 0xa6, 0x66, 0x8c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa5, 0xc9, 0xa2, + 0xc1, 0x71, 0xd9, 0xa2, 0x9b, 0x93, 0x49, 0x9a, 0x2c, 0x17, 0x1f, 0x45, + 0x33, 0x34, 0x66, 0x8b, 0x05, 0xc6, 0x6e, 0xa3, 0x75, 0x30, 0x90, 0x3a, + 0x9c, 0x51, 0x9a, 0xd2, 0xc6, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xda, 0x4c, + 0xd1, 0x60, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x02, 0x0f, 0x43, 0x9a, 0x37, + 0x01, 0xdf, 0x14, 0x58, 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x94, 0x58, + 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x80, 0x83, 0xd0, 0xe6, 0x8b, 0x05, + 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x3a, 0x9c, 0x51, 0x9a, 0x2c, 0x17, + 0x31, 0x3c, 0x5d, 0xe0, 0xcd, 0x2f, 0xc6, 0xb6, 0x2b, 0x6f, 0xa8, 0xc4, + 0x4b, 0x26, 0x4c, 0x53, 0xc6, 0x71, 0x24, 0x64, 0xf5, 0xc1, 0xfe, 0x87, + 0x8a, 0xe0, 0x6d, 0x3f, 0x67, 0x7d, 0x32, 0x2b, 0xb0, 0xf7, 0x1a, 0xad, + 0xcc, 0xf6, 0xe0, 0xe7, 0xca, 0x58, 0xd5, 0x09, 0x1e, 0x85, 0xb2, 0x7f, + 0x95, 0x7a, 0xd5, 0x25, 0x78, 0x38, 0xcc, 0x87, 0x2c, 0xcc, 0x2b, 0x2a, + 0xf8, 0x9a, 0x2a, 0x52, 0xef, 0xaa, 0xfb, 0xec, 0xd5, 0xfe, 0x77, 0x0b, + 0x95, 0xf4, 0xad, 0x32, 0xd3, 0x44, 0xb0, 0x86, 0xca, 0xc6, 0x05, 0xb7, + 0xb6, 0x88, 0x61, 0x23, 0x4e, 0x83, 0xfc, 0x4f, 0xbd, 0x7c, 0x6b, 0x5f, + 0x69, 0x57, 0xc5, 0xb5, 0xf9, 0x8f, 0x88, 0xb0, 0x8d, 0x38, 0xe0, 0xe1, + 0x05, 0x64, 0xb9, 0xec, 0x97, 0xfd, 0xb8, 0x34, 0x14, 0x51, 0x45, 0x7e, + 0x32, 0x30, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, + 0x28, 0x00, 0xaf, 0xad, 0xfc, 0x2b, 0x04, 0x57, 0x9e, 0x06, 0xd1, 0xad, + 0xe7, 0x8d, 0x66, 0x86, 0x4d, 0x3a, 0x04, 0x78, 0xdc, 0x65, 0x58, 0x18, + 0xd7, 0x20, 0x8a, 0xf9, 0x22, 0xbe, 0xba, 0xf0, 0x67, 0xfc, 0x89, 0xfa, + 0x17, 0xfd, 0x78, 0x41, 0xff, 0x00, 0xa2, 0xd6, 0xbf, 0x5c, 0xf0, 0xee, + 0x2a, 0x58, 0x9c, 0x42, 0x7b, 0x72, 0xaf, 0xcc, 0x47, 0x05, 0xab, 0x7e, + 0xcf, 0x7a, 0x4d, 0xdd, 0xdb, 0x4b, 0x65, 0xa8, 0x4f, 0x63, 0x13, 0x1c, + 0xf9, 0x25, 0x04, 0x80, 0x7b, 0x02, 0x48, 0x3f, 0x9e, 0x6b, 0xaf, 0xf0, + 0x57, 0xc3, 0xcd, 0x27, 0xc0, 0xd1, 0xb9, 0xb3, 0x47, 0x9a, 0xee, 0x41, + 0xb6, 0x4b, 0xa9, 0xb0, 0x5c, 0x8f, 0x41, 0xe8, 0x3d, 0x87, 0xe3, 0x5d, + 0x36, 0x69, 0x6b, 0xf5, 0x7c, 0x37, 0x0f, 0xe5, 0x78, 0x3a, 0xff, 0x00, + 0x59, 0xa1, 0x41, 0x46, 0x7d, 0xf5, 0xd3, 0xd1, 0x6c, 0xbe, 0x49, 0x0a, + 0xe3, 0xb7, 0x51, 0xba, 0x99, 0x9a, 0x03, 0x03, 0xde, 0xbd, 0xfb, 0x05, + 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x06, 0x4f, 0x14, 0x51, 0x60, 0xb8, + 0xfd, 0xd4, 0x6e, 0xa6, 0xd1, 0x45, 0x82, 0xe3, 0xb7, 0x51, 0xba, 0x98, + 0x08, 0x3d, 0x0e, 0x68, 0x24, 0x0e, 0xf4, 0x58, 0x2e, 0x3f, 0x75, 0x1b, + 0xa9, 0x94, 0xb4, 0x58, 0x2e, 0x3b, 0x75, 0x1b, 0xa9, 0x99, 0xa0, 0x10, + 0x7a, 0x1a, 0x2c, 0x17, 0x31, 0x62, 0xbd, 0x91, 0x46, 0xf7, 0x90, 0x3e, + 0xd3, 0xf7, 0x1b, 0xa9, 0xad, 0x58, 0xa5, 0x13, 0x46, 0xae, 0xbd, 0x08, + 0xac, 0x3f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, 0x91, 0x56, 0x52, 0xf1, + 0xe3, 0x48, 0x55, 0x42, 0x80, 0x7a, 0x80, 0x3d, 0xeb, 0xb2, 0x70, 0xbe, + 0xc7, 0x9d, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x36, 0x59, 0x44, 0x31, + 0xb3, 0xb7, 0x41, 0x49, 0xba, 0xb3, 0x1e, 0xf2, 0x49, 0x12, 0x65, 0x60, + 0x08, 0x1d, 0x01, 0x1e, 0xf5, 0x84, 0x61, 0xcc, 0x75, 0x4e, 0xa7, 0x22, + 0x1b, 0x2d, 0xec, 0x8c, 0x0b, 0xa4, 0x81, 0x37, 0x1f, 0xb8, 0xbd, 0x7e, + 0xb4, 0xb1, 0x5e, 0xc8, 0xa0, 0x3b, 0xb8, 0x7d, 0xa7, 0xee, 0x37, 0x5f, + 0xad, 0x57, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x14, 0x79, 0xc7, 0xfb, + 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xeb, 0xe5, 0x56, 0xb5, 0x8e, 0x0e, 0x77, + 0x7b, 0xdc, 0xdd, 0x8e, 0x41, 0x2a, 0x2b, 0xaf, 0x42, 0x33, 0x4e, 0xac, + 0xa8, 0xef, 0x1d, 0x3c, 0x85, 0x00, 0x05, 0x3d, 0x40, 0x1e, 0xe6, 0xb4, + 0x77, 0x57, 0x24, 0xa3, 0xca, 0x77, 0xc2, 0x7c, 0xe8, 0x74, 0x92, 0x08, + 0xd1, 0x99, 0x8f, 0x00, 0x66, 0xb2, 0xa5, 0xbd, 0x91, 0x81, 0x74, 0x70, + 0x99, 0x38, 0xd8, 0x3a, 0xfd, 0x69, 0xd2, 0x5e, 0x3b, 0xf9, 0xea, 0x40, + 0x2a, 0x3a, 0x02, 0x3d, 0xc5, 0x55, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, + 0x15, 0xbc, 0x21, 0x6d, 0xce, 0x6a, 0x95, 0x6f, 0xa2, 0x64, 0xf1, 0x5e, + 0x48, 0xa3, 0x7b, 0x48, 0x1f, 0x69, 0xfb, 0x8d, 0xd4, 0xd6, 0xac, 0x52, + 0x89, 0xa3, 0x57, 0x5e, 0x86, 0xb0, 0xfc, 0xe3, 0xfd, 0xc4, 0xff, 0x00, + 0xbe, 0x45, 0x59, 0x4b, 0xc7, 0x8d, 0x61, 0x0a, 0x14, 0x03, 0xd4, 0x01, + 0xef, 0x44, 0xe1, 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x15, + 0x1e, 0xea, 0x5d, 0xd5, 0xcd, 0x63, 0xba, 0xe3, 0xeb, 0xc0, 0xbc, 0x71, + 0xf0, 0x57, 0x55, 0x83, 0x57, 0x9e, 0xe7, 0x44, 0x85, 0x6f, 0x6c, 0xa6, + 0x72, 0xe2, 0x25, 0x70, 0xaf, 0x16, 0x4e, 0x76, 0xe0, 0x91, 0x91, 0xe9, + 0x8a, 0xf7, 0xad, 0xd4, 0x66, 0xbe, 0x7f, 0x39, 0xc8, 0xf0, 0x99, 0xe5, + 0x18, 0xd2, 0xc4, 0xdd, 0x72, 0xbb, 0xa6, 0xb7, 0x5d, 0xfb, 0xef, 0xe8, + 0x17, 0xb1, 0xf2, 0xc5, 0xff, 0x00, 0xc3, 0x5f, 0x12, 0xe9, 0x96, 0x53, + 0x5d, 0xdd, 0x69, 0x52, 0x43, 0x6f, 0x0a, 0x97, 0x92, 0x42, 0xe8, 0x42, + 0x81, 0xd4, 0xf0, 0x6b, 0x99, 0xaf, 0xaa, 0xfe, 0x21, 0x9c, 0xf8, 0x1f, + 0x5c, 0xff, 0x00, 0xaf, 0x47, 0xfe, 0x55, 0xf2, 0xa5, 0x7e, 0x01, 0xc5, + 0x59, 0x1e, 0x1f, 0x22, 0xc4, 0x53, 0xa3, 0x87, 0x93, 0x92, 0x94, 0x6f, + 0xef, 0x5b, 0xbd, 0xba, 0x24, 0x5a, 0x77, 0x0a, 0xe9, 0xec, 0xbe, 0x19, + 0xf8, 0x9b, 0x51, 0xb3, 0x86, 0xea, 0xdb, 0x49, 0x92, 0x5b, 0x79, 0x90, + 0x49, 0x1b, 0x87, 0x40, 0x19, 0x48, 0xc8, 0x3d, 0x6b, 0x98, 0xaf, 0xac, + 0x3c, 0x08, 0x71, 0xe0, 0xad, 0x0b, 0xfe, 0xbc, 0xa1, 0xff, 0x00, 0xd0, + 0x05, 0x57, 0x0a, 0xe4, 0x58, 0x7c, 0xf6, 0xbd, 0x5a, 0x58, 0x89, 0x4a, + 0x2a, 0x2a, 0xfe, 0xed, 0xbb, 0xdb, 0xaa, 0x60, 0xdd, 0x8f, 0x9e, 0xdb, + 0xe1, 0x4f, 0x8a, 0xd1, 0x4b, 0x1d, 0x1a, 0x50, 0x00, 0xc9, 0x3e, 0x62, + 0x7f, 0xf1, 0x55, 0xc9, 0xd7, 0xd9, 0x17, 0x4d, 0xfe, 0x8d, 0x37, 0xfb, + 0x87, 0xf9, 0x57, 0xc6, 0xf5, 0xd1, 0xc5, 0x9c, 0x3d, 0x86, 0xc8, 0x5d, + 0x05, 0x87, 0x9c, 0xa5, 0xcf, 0xcd, 0x7e, 0x6b, 0x74, 0xb6, 0xd6, 0x4b, + 0xb8, 0x27, 0x70, 0xae, 0x87, 0x47, 0xf0, 0x07, 0x88, 0x35, 0xfb, 0x04, + 0xbd, 0xb0, 0xd3, 0x64, 0xb9, 0xb5, 0x72, 0x42, 0xc8, 0xae, 0xa0, 0x12, + 0x0e, 0x0f, 0x53, 0xeb, 0x5c, 0xf5, 0x7d, 0x27, 0xf0, 0x54, 0xe3, 0xe1, + 0xed, 0x8f, 0xfd, 0x74, 0x97, 0xff, 0x00, 0x43, 0x35, 0xe7, 0x70, 0xbe, + 0x4f, 0x43, 0x3c, 0xc6, 0xcb, 0x0d, 0x5e, 0x4d, 0x25, 0x16, 0xf4, 0xb5, + 0xee, 0x9a, 0x5d, 0x53, 0xee, 0x0d, 0xd8, 0xf3, 0x1f, 0x0d, 0x7c, 0x12, + 0xd7, 0xb5, 0x3d, 0x42, 0x31, 0xa9, 0x40, 0x34, 0xdb, 0x20, 0xc0, 0xc8, + 0xee, 0xea, 0xce, 0x47, 0x70, 0xa0, 0x13, 0xcf, 0xb9, 0xe2, 0xbe, 0x87, + 0xb7, 0x82, 0x3b, 0x4b, 0x78, 0xa0, 0x89, 0x42, 0x45, 0x12, 0x84, 0x45, + 0x1d, 0x80, 0x18, 0x02, 0x97, 0x34, 0x9b, 0xab, 0xfa, 0x07, 0x25, 0xe1, + 0xfc, 0x1e, 0x45, 0x09, 0x47, 0x0d, 0x76, 0xe5, 0xbb, 0x7a, 0xbd, 0x36, + 0x5a, 0x24, 0xad, 0xf2, 0x22, 0xf7, 0x24, 0xcd, 0x19, 0xa8, 0xf7, 0x51, + 0xba, 0xbe, 0x96, 0xc1, 0x71, 0xd2, 0x48, 0x22, 0x46, 0x76, 0xe0, 0x0e, + 0x6b, 0x2a, 0x5b, 0xd9, 0x18, 0x17, 0x47, 0x11, 0xe4, 0xe3, 0x60, 0xeb, + 0xf5, 0xa7, 0x49, 0x78, 0xef, 0xe7, 0xa9, 0x00, 0xa8, 0xe8, 0x08, 0xf7, + 0x02, 0xaa, 0xf9, 0xc7, 0xfb, 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xe9, 0x84, + 0x2d, 0xb9, 0xc5, 0x56, 0xaf, 0x36, 0x89, 0x93, 0xc5, 0x7b, 0x22, 0x8d, + 0xef, 0x20, 0x7d, 0xa7, 0xee, 0x37, 0x53, 0x5a, 0xd1, 0x4a, 0x26, 0x8d, + 0x5d, 0x7a, 0x1a, 0xc2, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x15, 0x65, + 0x2f, 0x1e, 0x34, 0x84, 0x28, 0x50, 0x09, 0xe4, 0x01, 0xef, 0x44, 0xe1, + 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xa6, 0xcb, 0x28, 0x86, 0x36, + 0x76, 0xe8, 0x29, 0x37, 0x56, 0x63, 0xde, 0x3c, 0x89, 0x30, 0x60, 0xa4, + 0x0e, 0x80, 0x8f, 0x7a, 0xc2, 0x30, 0xe6, 0x3a, 0xa7, 0x53, 0x91, 0x0d, + 0x96, 0xf6, 0x46, 0x05, 0xd2, 0x40, 0x9b, 0x8f, 0xdc, 0x5e, 0xa3, 0xde, + 0x96, 0x2b, 0xd9, 0x14, 0x07, 0x77, 0x0f, 0xb4, 0xfd, 0xc6, 0xeb, 0xf5, + 0xaa, 0xfe, 0x71, 0xfe, 0xe2, 0x7f, 0xdf, 0x22, 0x8f, 0x38, 0xff, 0x00, + 0x71, 0x3f, 0xef, 0x91, 0x5d, 0x7c, 0xaa, 0xd6, 0xb1, 0xc1, 0xce, 0xef, + 0x7b, 0x9b, 0xb1, 0xc8, 0x25, 0x45, 0x75, 0xe8, 0x46, 0x69, 0xd5, 0x95, + 0x1d, 0xe3, 0xa0, 0x81, 0x46, 0x02, 0x9e, 0xa0, 0x0f, 0x72, 0x2b, 0x4b, + 0x35, 0xc9, 0x28, 0xf2, 0x9d, 0xf0, 0xa9, 0xce, 0x85, 0x92, 0x41, 0x1a, + 0x33, 0xb7, 0x40, 0x32, 0x6b, 0x2a, 0x5b, 0xd9, 0x1c, 0x17, 0x47, 0x09, + 0x93, 0x8d, 0x83, 0xaf, 0xd6, 0x9d, 0x25, 0xe3, 0xbf, 0x9e, 0xa4, 0x02, + 0xa3, 0xa0, 0x23, 0xdc, 0x55, 0x5f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, + 0x91, 0x5b, 0xc2, 0x16, 0xdc, 0xe6, 0xa9, 0x57, 0x9b, 0x44, 0xc8, 0xea, + 0x70, 0xa4, 0x88, 0x30, 0x09, 0xff, 0x00, 0xf5, 0xd3, 0x4d, 0xbb, 0x09, + 0x84, 0x59, 0x1b, 0xbd, 0x7b, 0x56, 0x95, 0xb4, 0x66, 0x18, 0x42, 0x13, + 0x92, 0x3d, 0x2a, 0xe5, 0x2b, 0x23, 0x2a, 0x70, 0x72, 0x6d, 0x32, 0x7a, + 0xc8, 0x2a, 0x40, 0x9f, 0x20, 0x8f, 0xff, 0x00, 0x5d, 0x6a, 0xe6, 0xa3, + 0xb9, 0x8c, 0xcf, 0x09, 0x40, 0x40, 0x27, 0xd6, 0xb1, 0x83, 0xe5, 0x3a, + 0x6a, 0x47, 0x99, 0x5c, 0xc7, 0xa2, 0xa5, 0x5b, 0x76, 0x69, 0x8c, 0x59, + 0x1b, 0xbf, 0x4a, 0x3e, 0xce, 0xde, 0x7f, 0x95, 0x91, 0xbb, 0xd7, 0xb5, + 0x74, 0xdd, 0x1c, 0x3c, 0xac, 0x7a, 0xa9, 0x2d, 0x6f, 0x80, 0x7f, 0xcb, + 0x1a, 0xd6, 0xa8, 0x6d, 0xe3, 0x30, 0xc2, 0xa8, 0x48, 0x24, 0x77, 0x15, + 0x26, 0x6b, 0x9a, 0x6f, 0x99, 0x9d, 0xf4, 0xe3, 0xca, 0x8c, 0xb6, 0x52, + 0x1a, 0xe3, 0x20, 0xff, 0x00, 0x96, 0x15, 0x5e, 0xb6, 0x2e, 0x23, 0x33, + 0x44, 0xc8, 0x08, 0x04, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, + 0x7a, 0xf6, 0xad, 0xa1, 0x24, 0xd1, 0xcb, 0x52, 0x0e, 0x2d, 0x58, 0x8a, + 0xa7, 0x0a, 0x48, 0x83, 0x00, 0x9f, 0xff, 0x00, 0x5d, 0x34, 0xdb, 0xb2, + 0xcc, 0x22, 0xc8, 0xdd, 0xeb, 0xda, 0xb4, 0xad, 0xa3, 0x30, 0xc2, 0xaa, + 0x4e, 0x48, 0xf4, 0xa2, 0x52, 0xb2, 0x0a, 0x70, 0x72, 0x6d, 0x32, 0x7a, + 0x29, 0xa4, 0xd1, 0x9a, 0xe5, 0xb1, 0xde, 0x3a, 0x8a, 0x6e, 0x73, 0x41, + 0x34, 0x58, 0x0e, 0x7f, 0xe2, 0x1f, 0xfc, 0x88, 0xfa, 0xe7, 0xfd, 0x7a, + 0xbf, 0xf2, 0xaf, 0x95, 0xab, 0xeb, 0x2f, 0x18, 0x58, 0x4b, 0xaa, 0xf8, + 0x57, 0x56, 0xb4, 0x80, 0x6e, 0x9a, 0x6b, 0x69, 0x15, 0x17, 0xd5, 0xb6, + 0x9c, 0x0f, 0xce, 0xbe, 0x4e, 0x65, 0x2a, 0x48, 0x20, 0x82, 0x38, 0x20, + 0xf6, 0xaf, 0xc1, 0x7c, 0x46, 0x84, 0x96, 0x32, 0x84, 0xed, 0xa3, 0x8b, + 0x5f, 0x73, 0xff, 0x00, 0x82, 0x8d, 0x20, 0x25, 0x7d, 0x5d, 0xe0, 0x5f, + 0xf9, 0x12, 0xf4, 0x2f, 0xfa, 0xf2, 0x87, 0xff, 0x00, 0x40, 0x15, 0xf2, + 0x9a, 0x23, 0x48, 0xea, 0x88, 0xa5, 0x9d, 0x8e, 0x02, 0x81, 0x92, 0x4d, + 0x7d, 0x69, 0xe1, 0x8b, 0x19, 0x34, 0xbf, 0x0d, 0xe9, 0x76, 0x73, 0x71, + 0x2c, 0x16, 0xd1, 0xc6, 0xe3, 0xd0, 0x85, 0x00, 0xd5, 0x78, 0x73, 0x09, + 0x3c, 0x56, 0x22, 0x76, 0xd1, 0x45, 0x2f, 0xc7, 0xfe, 0x00, 0x4c, 0xd0, + 0xba, 0xff, 0x00, 0x8f, 0x69, 0xbf, 0xdc, 0x3f, 0xca, 0xbe, 0x39, 0xaf, + 0xb1, 0xe5, 0x5f, 0x32, 0x27, 0x4c, 0xe3, 0x72, 0x91, 0x9a, 0xf9, 0x03, + 0x50, 0xb1, 0x9b, 0x4c, 0xbe, 0xb8, 0xb4, 0x9d, 0x0a, 0x4d, 0x04, 0x86, + 0x37, 0x53, 0xd8, 0x83, 0x8a, 0xed, 0xf1, 0x22, 0x12, 0xff, 0x00, 0x65, + 0x9d, 0xb4, 0xf7, 0xd7, 0xfe, 0x92, 0x10, 0x2b, 0xd7, 0xd2, 0x5f, 0x05, + 0xbf, 0xe4, 0x9f, 0x58, 0xff, 0x00, 0xd7, 0x49, 0x7f, 0xf4, 0x33, 0x5f, + 0x36, 0xd7, 0xd3, 0x9f, 0x0a, 0x74, 0xe9, 0xb4, 0xbf, 0x01, 0xe9, 0x91, + 0x4e, 0xa5, 0x24, 0x75, 0x69, 0x76, 0x9e, 0xa0, 0x33, 0x12, 0x3f, 0x42, + 0x2b, 0xc3, 0xf0, 0xf6, 0x12, 0x96, 0x69, 0x52, 0x49, 0x68, 0xa0, 0xff, + 0x00, 0x19, 0x44, 0x72, 0xd8, 0xeb, 0xe8, 0xa6, 0xe6, 0x8c, 0xd7, 0xf4, + 0x3d, 0x8c, 0x87, 0x51, 0x4d, 0xcd, 0x19, 0xa2, 0xc0, 0x65, 0xb2, 0x90, + 0xd7, 0x19, 0x04, 0x7f, 0xfb, 0x42, 0xab, 0xd6, 0xc5, 0xc4, 0x66, 0x68, + 0x59, 0x07, 0x04, 0xfa, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, + 0x7a, 0xf6, 0xae, 0xa8, 0x4a, 0xe8, 0xe0, 0xa9, 0x07, 0x16, 0xac, 0x45, + 0x53, 0x85, 0x24, 0x41, 0x80, 0x4f, 0xff, 0x00, 0xae, 0x9a, 0x6d, 0xd9, + 0x66, 0x11, 0x64, 0x6e, 0xf5, 0xed, 0x5a, 0x76, 0xd1, 0x98, 0x21, 0x0a, + 0x48, 0x27, 0xda, 0x89, 0x4a, 0xc8, 0x29, 0xc1, 0xc9, 0xb4, 0xc9, 0xab, + 0x20, 0xa9, 0x02, 0x7c, 0x82, 0x3f, 0xfd, 0x75, 0xab, 0x9a, 0x8a, 0xe6, + 0x33, 0x34, 0x45, 0x41, 0xc1, 0xf7, 0xac, 0x60, 0xf9, 0x4e, 0x9a, 0x91, + 0xe6, 0x46, 0x45, 0x15, 0x28, 0xb7, 0x66, 0x98, 0xc5, 0x91, 0xb8, 0x77, + 0xed, 0x47, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, 0x7a, 0xf6, 0xae, 0x9b, + 0xa3, 0x87, 0x95, 0x8f, 0x55, 0x25, 0xad, 0xf0, 0x0f, 0xf9, 0x63, 0x5a, + 0xd5, 0x0d, 0xba, 0x18, 0x61, 0x54, 0x24, 0x12, 0x3d, 0x2a, 0x4c, 0xd7, + 0x34, 0xdf, 0x33, 0x3b, 0xe9, 0xc7, 0x95, 0x19, 0x6c, 0xa4, 0x35, 0xc6, + 0x41, 0xff, 0x00, 0x2c, 0x2a, 0xbd, 0x6c, 0x5c, 0x46, 0x66, 0x85, 0x90, + 0x1c, 0x13, 0xeb, 0x59, 0x9f, 0x67, 0x6f, 0x3f, 0xca, 0xc8, 0xdd, 0xfa, + 0x56, 0xd0, 0x92, 0x68, 0xe5, 0xa9, 0x06, 0x9a, 0xb1, 0xa0, 0xd6, 0xc1, + 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, 0x6a, 0x28, 0xae, 0x76, 0xdb, 0x3a, + 0xd2, 0xb6, 0xc1, 0x45, 0x14, 0x52, 0x19, 0x0a, 0xdb, 0x05, 0xb8, 0x32, + 0xe4, 0xe4, 0xf6, 0xa3, 0xec, 0xc3, 0xed, 0x1e, 0x6e, 0x4e, 0x7d, 0x2a, + 0x6a, 0x2a, 0xb9, 0x99, 0x3c, 0xa8, 0x28, 0xa2, 0x8a, 0x92, 0x82, 0xa1, + 0xfb, 0x30, 0xfb, 0x47, 0x9b, 0x93, 0x9f, 0x4a, 0x9a, 0x8a, 0x69, 0xd8, + 0x4d, 0x5f, 0x72, 0x16, 0xb6, 0x0d, 0x70, 0x25, 0xc9, 0xc8, 0xed, 0x53, + 0x51, 0x45, 0x0d, 0xb6, 0x09, 0x5b, 0x60, 0xa2, 0x8a, 0x29, 0x0c, 0x28, + 0xa2, 0x8a, 0x00, 0x2b, 0x83, 0xf1, 0x57, 0xc1, 0xdd, 0x1b, 0xc4, 0xb7, + 0xaf, 0x79, 0x1b, 0xcb, 0xa7, 0x5d, 0x48, 0x73, 0x21, 0x80, 0x02, 0x8e, + 0x7d, 0x4a, 0x9e, 0xff, 0x00, 0x42, 0x2b, 0xbc, 0xa2, 0xbc, 0xfc, 0x76, + 0x5f, 0x85, 0xcc, 0xa9, 0xfb, 0x1c, 0x5d, 0x35, 0x38, 0xf9, 0xfe, 0x8f, + 0x75, 0xf2, 0x1a, 0x6d, 0x1c, 0x3f, 0x84, 0xbe, 0x11, 0xe8, 0xde, 0x16, + 0xbb, 0x4b, 0xc2, 0xd2, 0x5f, 0xde, 0x27, 0x29, 0x24, 0xf8, 0xda, 0x87, + 0xd5, 0x54, 0x77, 0xf7, 0x39, 0xae, 0xe2, 0x8a, 0x29, 0xe0, 0xb0, 0x18, + 0x5c, 0xba, 0x97, 0xb1, 0xc2, 0x53, 0x50, 0x8f, 0x97, 0xeb, 0xd5, 0xfc, + 0xc2, 0xed, 0x85, 0x71, 0xfe, 0x31, 0xf8, 0x5f, 0xa4, 0x78, 0xc6, 0x6f, + 0xb4, 0xcd, 0xe6, 0x5a, 0x5e, 0xe3, 0x06, 0xe2, 0x0c, 0x65, 0xc7, 0x6d, + 0xc0, 0xf5, 0xfe, 0x7e, 0xf5, 0xd8, 0x51, 0x55, 0x8c, 0xc1, 0x61, 0xf1, + 0xf4, 0x9d, 0x0c, 0x54, 0x14, 0xe2, 0xfa, 0x3f, 0xeb, 0x46, 0x17, 0x68, + 0xf3, 0xbf, 0x0f, 0x7c, 0x11, 0xd1, 0x74, 0x6b, 0xc4, 0xb9, 0xb9, 0x96, + 0x5d, 0x49, 0xd0, 0xe5, 0x63, 0x94, 0x05, 0x8f, 0x3e, 0xa5, 0x47, 0x5f, + 0xc4, 0xe3, 0xda, 0xbd, 0x13, 0xa5, 0x14, 0x56, 0x38, 0x1c, 0xb7, 0x07, + 0x96, 0x41, 0xd3, 0xc1, 0xd3, 0x50, 0x4f, 0x7b, 0x75, 0xf5, 0x7b, 0xb0, + 0x6d, 0xb0, 0xa2, 0x8a, 0x2b, 0xd3, 0x10, 0x51, 0x45, 0x14, 0x00, 0x54, + 0x3f, 0x66, 0x1f, 0x68, 0xf3, 0x72, 0x73, 0xe9, 0x53, 0x51, 0x4d, 0x3b, + 0x09, 0xab, 0xee, 0x42, 0xd6, 0xc1, 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, + 0x6a, 0x28, 0xa1, 0xb6, 0xc1, 0x2b, 0x6c, 0x14, 0x51, 0x45, 0x21, 0x90, + 0xad, 0xb0, 0x5b, 0x83, 0x2e, 0x4e, 0x4f, 0x6a, 0x3e, 0xcc, 0x3e, 0xd1, + 0xe6, 0xe4, 0xe7, 0xd2, 0xa6, 0xa2, 0xab, 0x99, 0x93, 0xca, 0x82, 0x8a, + 0x28, 0xa9, 0x28, 0x2a, 0x1f, 0xb3, 0x0f, 0xb4, 0x79, 0xb9, 0x39, 0xf4, + 0xa9, 0xa8, 0xa6, 0x9d, 0x84, 0xd5, 0xf7, 0x3e, 0x20, 0xff, 0x00, 0x87, + 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, + 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, + 0xbe, 0x17, 0xfe, 0xc6, 0xb2, 0xff, 0x00, 0x9f, 0x64, 0xa7, 0x47, 0xa2, + 0x59, 0x31, 0xe6, 0xd9, 0x31, 0x45, 0x8e, 0x9e, 0x44, 0x7d, 0xcd, 0xff, + 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45, + 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, + 0x15, 0x7c, 0x3b, 0xfd, 0x87, 0x61, 0xff, 0x00, 0x3e, 0xb1, 0xfe, 0x54, + 0xd9, 0x34, 0x6b, 0x05, 0x1c, 0x5b, 0x47, 0x9f, 0xa5, 0x16, 0x0e, 0x44, + 0x7d, 0xc9, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, + 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, + 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x2f, 0xfd, 0x8d, 0x65, 0xff, 0x00, 0x3e, + 0xc9, 0xf9, 0x53, 0xe3, 0xd1, 0x2c, 0x4f, 0x26, 0xd9, 0x31, 0xf4, 0xa2, + 0xc1, 0xc8, 0x8f, 0xb9, 0x7f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1, + 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, 0xa6, 0xf8, + 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, 0x87, 0x7f, + 0xb0, 0xec, 0x3f, 0xe7, 0xd6, 0x3f, 0xca, 0x99, 0x26, 0x8d, 0x62, 0x38, + 0x16, 0xc9, 0x9a, 0x2c, 0x1c, 0x88, 0xfb, 0x97, 0xfe, 0x1e, 0x9b, 0xe1, + 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0x8f, 0xf8, 0x7a, 0x6f, + 0x85, 0xbf, 0xe8, 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0xf8, 0x5f, 0xfb, + 0x1a, 0xcb, 0xfe, 0x7d, 0x93, 0xf2, 0xa9, 0x23, 0xd1, 0x2c, 0x48, 0xc9, + 0xb6, 0x4f, 0xca, 0x8b, 0x07, 0x22, 0x3e, 0xe4, 0xff, 0x00, 0x87, 0xa6, + 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, + 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, + 0x1d, 0xfe, 0xc3, 0xb0, 0xff, 0x00, 0x9f, 0x58, 0xff, 0x00, 0x2a, 0x64, + 0x9a, 0x35, 0x88, 0x38, 0x16, 0xc9, 0xf9, 0x51, 0x60, 0xe4, 0x47, 0xdc, + 0xbf, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, + 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, + 0x00, 0x81, 0x51, 0x57, 0xc2, 0xff, 0x00, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, + 0x9f, 0x95, 0x48, 0x9a, 0x1d, 0x89, 0x19, 0x36, 0xc9, 0xf9, 0x51, 0x60, + 0xe4, 0x47, 0xdc, 0x9f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, + 0x7f, 0xe0, 0x54, 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, + 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, 0x57, 0xc3, 0xbf, 0xd8, 0x76, 0x1f, + 0xf3, 0xeb, 0x1f, 0xe5, 0x51, 0xbe, 0x8d, 0x63, 0x9c, 0x0b, 0x64, 0xfc, + 0xa8, 0xb0, 0x72, 0x23, 0xee, 0x6f, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, + 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, + 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x7f, 0xec, + 0x7b, 0x2f, 0xf9, 0xf6, 0x4f, 0xca, 0x9b, 0x2e, 0x9d, 0xa7, 0xdb, 0xed, + 0x53, 0x67, 0xe6, 0xc8, 0xc0, 0x90, 0xb1, 0xae, 0x4e, 0x3d, 0x68, 0xb0, + 0x72, 0x23, 0xee, 0xaf, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, 0x47, 0xd6, + 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1, + 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x1f, 0x23, 0x4b, 0xd8, + 0x1b, 0xec, 0x44, 0x8d, 0xbb, 0x9b, 0x09, 0xf7, 0x06, 0x48, 0xc9, 0xe7, + 0xd8, 0xfe, 0x54, 0x3d, 0x9e, 0x9e, 0x2e, 0x04, 0x66, 0xcb, 0x60, 0x27, + 0x68, 0x72, 0xbf, 0x29, 0x3f, 0x5c, 0xd1, 0x60, 0xe4, 0x47, 0xdd, 0xdf, + 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x54, + 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, + 0x81, 0x51, 0x57, 0xc1, 0x82, 0xdf, 0x4e, 0x1b, 0xb7, 0xd9, 0x98, 0x88, + 0x19, 0xda, 0xeb, 0xc9, 0xe7, 0x1c, 0x73, 0xea, 0x45, 0x4d, 0x1d, 0x9e, + 0x9a, 0x46, 0x0d, 0x91, 0x12, 0x02, 0x41, 0x8c, 0xaf, 0xcd, 0x9c, 0x67, + 0xd7, 0xd2, 0x8b, 0x07, 0x22, 0x3e, 0xed, 0xff, 0x00, 0x87, 0xa6, 0xf8, + 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, + 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x16, + 0xb7, 0xd3, 0xf4, 0xeb, 0x86, 0x65, 0xfb, 0x1f, 0x96, 0xea, 0x01, 0x2b, + 0x22, 0xe0, 0xe0, 0xf7, 0xa7, 0x36, 0x8f, 0x62, 0x49, 0xc5, 0xb2, 0x62, + 0x8b, 0x07, 0x22, 0x3e, 0xe7, 0xff, 0x00, 0x87, 0xa6, 0xf8, 0x5b, 0xfe, + 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, + 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x17, 0xfe, 0xc6, + 0xb2, 0x3f, 0xf2, 0xec, 0x95, 0x30, 0xd0, 0xec, 0x40, 0xff, 0x00, 0x8f, + 0x64, 0xa2, 0xc1, 0xc8, 0x8f, 0xb8, 0xbf, 0xe1, 0xe9, 0xbe, 0x16, 0xff, + 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, + 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, + 0x87, 0x7f, 0xb1, 0x2c, 0x07, 0xfc, 0xba, 0xc7, 0xf9, 0x54, 0x27, 0x47, + 0xb2, 0x27, 0xfe, 0x3d, 0x92, 0x8b, 0x07, 0x22, 0x3e, 0xe8, 0xff, 0x00, + 0x87, 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, + 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, + 0x8a, 0xbe, 0x17, 0x1a, 0x2d, 0x91, 0x38, 0xfb, 0x32, 0x54, 0xdf, 0xd8, + 0x76, 0x1f, 0xf3, 0xea, 0x9f, 0x95, 0x16, 0x0e, 0x44, 0x7d, 0xc5, 0xff, + 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45, + 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, + 0x15, 0x7c, 0x3a, 0x74, 0x4b, 0x00, 0x33, 0xf6, 0x58, 0xff, 0x00, 0x2a, + 0x84, 0xe8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x47, + 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, + 0x1f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, + 0x55, 0xf0, 0xc2, 0xe8, 0xb6, 0x4c, 0x40, 0xfb, 0x32, 0x54, 0xbf, 0xd8, + 0x76, 0x1f, 0xf3, 0xea, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x17, 0xfc, 0x3d, + 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x1f, 0xf0, + 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x55, 0xf0, + 0xe3, 0x68, 0xb6, 0x0a, 0x09, 0xfb, 0x2c, 0x7f, 0x95, 0x45, 0xfd, 0x8d, + 0x65, 0xff, 0x00, 0x3e, 0xc9, 0x45, 0x83, 0x91, 0x1f, 0x74, 0x7f, 0xc3, + 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, + 0x51, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, + 0x05, 0x45, 0x5f, 0x0c, 0x26, 0x89, 0x64, 0xcd, 0xff, 0x00, 0x1e, 0xc9, + 0x52, 0xff, 0x00, 0x61, 0xd8, 0x7f, 0xcf, 0xac, 0x7f, 0x95, 0x16, 0x0e, + 0x44, 0x7d, 0xc5, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, + 0xc7, 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, + 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x38, 0xfa, 0x2d, 0x82, 0xaf, 0xfc, + 0x7a, 0xc7, 0xf9, 0x54, 0x5f, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, + 0x39, 0x11, 0x72, 0xa7, 0x45, 0xda, 0xb8, 0xa8, 0xe3, 0x5c, 0xb6, 0x7d, + 0x2a, 0x5a, 0xa2, 0xc2, 0xa1, 0x66, 0xdc, 0xd9, 0xa7, 0xc8, 0xd8, 0x18, + 0xf5, 0xa8, 0xa8, 0x01, 0x40, 0xc9, 0xa9, 0xd4, 0x6d, 0x18, 0xa8, 0xe2, + 0x5e, 0x73, 0x52, 0x50, 0x00, 0x4e, 0x05, 0x40, 0xc7, 0x71, 0xcd, 0x49, + 0x2b, 0x71, 0x8a, 0x8a, 0x80, 0x14, 0x0c, 0x9c, 0x54, 0xe0, 0x60, 0x62, + 0xa3, 0x89, 0x7b, 0xd4, 0x94, 0x00, 0x13, 0x80, 0x4d, 0x40, 0x4e, 0x4e, + 0x69, 0xf2, 0xb7, 0x6a, 0x8e, 0x80, 0x15, 0x46, 0xe3, 0x8a, 0x9e, 0x99, + 0x1a, 0xe0, 0x67, 0xd6, 0x9f, 0x40, 0x08, 0xc7, 0x68, 0xcd, 0x41, 0x4f, + 0x91, 0xb2, 0x71, 0xe9, 0x4c, 0xa0, 0x07, 0x22, 0xee, 0x6f, 0x6a, 0x27, + 0xb5, 0x59, 0xa4, 0x49, 0x37, 0xbc, 0x6e, 0xbc, 0x06, 0x43, 0x8c, 0x8f, + 0x43, 0xed, 0x52, 0x46, 0xb8, 0x5f, 0x73, 0x4e, 0xa0, 0x0a, 0xa6, 0xca, + 0x38, 0xa2, 0x95, 0x41, 0x6f, 0xde, 0x2e, 0xc3, 0xcf, 0x6c, 0x93, 0xff, + 0x00, 0xb3, 0x1a, 0x87, 0xec, 0x60, 0xca, 0xae, 0xd2, 0x48, 0xe1, 0x4e, + 0x42, 0x12, 0x36, 0x83, 0xf9, 0x55, 0xa9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, + 0x00, 0x57, 0x8b, 0x49, 0xb7, 0x0c, 0xfb, 0x53, 0xcb, 0x0c, 0x00, 0x21, + 0x38, 0xe8, 0x72, 0x0f, 0xd6, 0xa6, 0x5d, 0x39, 0x17, 0x90, 0xf2, 0x79, + 0x99, 0x2c, 0x64, 0x27, 0x2c, 0x4e, 0xdc, 0x7e, 0x82, 0xac, 0xa2, 0xed, + 0x5a, 0x5a, 0x00, 0xad, 0x1d, 0xaa, 0xdb, 0x16, 0x6f, 0x31, 0xe5, 0x91, + 0xc0, 0x05, 0xe4, 0x39, 0x38, 0x1d, 0xbf, 0x5a, 0x5a, 0x73, 0xb6, 0xe6, + 0xa6, 0xf5, 0xa0, 0x07, 0xc4, 0xb9, 0x39, 0xf4, 0xa9, 0x69, 0x14, 0x6d, + 0x18, 0xa5, 0xe9, 0x40, 0x0c, 0x95, 0xb0, 0x31, 0x51, 0x52, 0xb1, 0xdc, + 0x49, 0xa0, 0x0c, 0x9c, 0x50, 0x03, 0xe2, 0x5e, 0xf5, 0x25, 0x00, 0x60, + 0x62, 0x82, 0x70, 0x33, 0x40, 0x11, 0xca, 0xdd, 0xaa, 0x3a, 0x52, 0x72, + 0x73, 0x42, 0x8d, 0xc4, 0x0a, 0x00, 0x92, 0x25, 0xc0, 0xcd, 0x3e, 0x8e, + 0x94, 0x8c, 0x76, 0x82, 0x68, 0x02, 0x39, 0x5b, 0x27, 0x1e, 0x94, 0xca, + 0x3a, 0xd3, 0x91, 0x77, 0x35, 0x00, 0x49, 0x1a, 0xe1, 0x7d, 0xcd, 0x3a, + 0x8a, 0x47, 0x6d, 0xab, 0x40, 0x11, 0xc8, 0xd9, 0x6f, 0x61, 0x4c, 0xa2, + 0x9d, 0x1a, 0xee, 0x6f, 0x61, 0x40, 0x1f, 0xff, 0xd9 +}; diff --git a/services/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp new file mode 100644 index 0000000..6749899 --- /dev/null +++ b/services/camera/libcameraservice/FakeCamera.cpp @@ -0,0 +1,430 @@ +/* +** +** Copyright 2008, 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 "FakeCamera" +#include <utils/Log.h> + +#include <string.h> +#include <stdlib.h> +#include <utils/String8.h> + +#include "FakeCamera.h" + + +namespace android { + +// TODO: All this rgb to yuv should probably be in a util class. + +// TODO: I think something is wrong in this class because the shadow is kBlue +// and the square color should alternate between kRed and kGreen. However on the +// emulator screen these are all shades of gray. Y seems ok but the U and V are +// probably not. + +static int tables_initialized = 0; +uint8_t *gYTable, *gCbTable, *gCrTable; + +static int +clamp(int x) +{ + if (x > 255) return 255; + if (x < 0) return 0; + return x; +} + +/* the equation used by the video code to translate YUV to RGB looks like this + * + * Y = (Y0 - 16)*k0 + * Cb = Cb0 - 128 + * Cr = Cr0 - 128 + * + * G = ( Y - k1*Cr - k2*Cb ) + * R = ( Y + k3*Cr ) + * B = ( Y + k4*Cb ) + * + */ + +static const double k0 = 1.164; +static const double k1 = 0.813; +static const double k2 = 0.391; +static const double k3 = 1.596; +static const double k4 = 2.018; + +/* let's try to extract the value of Y + * + * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 ) + * + * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4) + * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16 + * + * let define: + * kYr = k1/k3 + * kYb = k2/k4 + * kYy = k0 * ( 1 + kYr + kYb ) + * + * we have: + * Y = ( G + kYr*R + kYb*B ) + * Y0 = clamp[ Y/kYy + 16 ] + */ + +static const double kYr = k1/k3; +static const double kYb = k2/k4; +static const double kYy = k0*( 1. + kYr + kYb ); + +static void +initYtab( void ) +{ + const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 ); + int i; + + gYTable = (uint8_t *)malloc(imax); + + for(i=0; i<imax; i++) { + int x = (int)(i/kYy + 16.5); + if (x < 16) x = 16; + else if (x > 235) x = 235; + gYTable[i] = (uint8_t) x; + } +} + +/* + * the source is RGB565, so adjust for 8-bit range of input values: + * + * G = (pixels >> 3) & 0xFC; + * R = (pixels >> 8) & 0xF8; + * B = (pixels & 0x1f) << 3; + * + * R2 = (pixels >> 11) R = R2*8 + * B2 = (pixels & 0x1f) B = B2*8 + * + * kYr*R = kYr2*R2 => kYr2 = kYr*8 + * kYb*B = kYb2*B2 => kYb2 = kYb*8 + * + * we want to use integer multiplications: + * + * SHIFT1 = 9 + * + * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1) + * + * ALPHA = kYr*(1 << (SHIFT1+3)) + * BETA = kYb*(1 << (SHIFT1+3)) + */ + +static const int SHIFT1 = 9; +static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 ); +static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 ); + +/* + * now let's try to get the values of Cb and Cr + * + * R-B = (k3*Cr - k4*Cb) + * + * k3*Cr = k4*Cb + (R-B) + * k4*Cb = k3*Cr - (R-B) + * + * R-G = (k1+k3)*Cr + k2*Cb + * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0) + * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B) + * + * kRr*Cr = (R-G) + kYb*(R-B) + * + * Cr = ((R-G) + kYb*(R-B))/kRr + * Cr0 = clamp(Cr + 128) + */ + +static const double kRr = (k1 + k3 + k2*k3/k4); + +static void +initCrtab( void ) +{ + uint8_t *pTable; + int i; + + gCrTable = (uint8_t *)malloc(768*2); + + pTable = gCrTable + 384; + for(i=-384; i<384; i++) + pTable[i] = (uint8_t) clamp( i/kRr + 128.5 ); +} + +/* + * B-G = (k2 + k4)*Cb + k1*Cr + * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B)) + * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B) + * + * kBb*Cb = (B-G) - kYr*(R-B) + * + * Cb = ((B-G) - kYr*(R-B))/kBb + * Cb0 = clamp(Cb + 128) + * + */ + +static const double kBb = (k2 + k4 + k1*k4/k3); + +static void +initCbtab( void ) +{ + uint8_t *pTable; + int i; + + gCbTable = (uint8_t *)malloc(768*2); + + pTable = gCbTable + 384; + for(i=-384; i<384; i++) + pTable[i] = (uint8_t) clamp( i/kBb + 128.5 ); +} + +/* + * SHIFT2 = 16 + * + * DELTA = kYb*(1 << SHIFT2) + * GAMMA = kYr*(1 << SHIFT2) + */ + +static const int SHIFT2 = 16; +static const int DELTA = kYb*(1 << SHIFT2); +static const int GAMMA = kYr*(1 << SHIFT2); + +int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[]) +{ + uint16_t *inputRGB = (uint16_t*)rgb16; + uint8_t *outYUV = yuv422; + int32_t width_dst = param[0]; + int32_t height_dst = param[1]; + int32_t pitch_dst = param[2]; + int32_t mheight_dst = param[3]; + int32_t pitch_src = param[4]; + uint8_t *y_tab = table[0]; + uint8_t *cb_tab = table[1]; + uint8_t *cr_tab = table[2]; + + int32_t size16 = pitch_dst*mheight_dst; + int32_t i,j,count; + int32_t ilimit,jlimit; + uint8_t *tempY,*tempU,*tempV; + uint16_t pixels; + int tmp; +uint32_t temp; + + tempY = outYUV; + tempU = outYUV + (height_dst * pitch_dst); + tempV = tempU + 1; + + jlimit = height_dst; + ilimit = width_dst; + + for(j=0; j<jlimit; j+=1) + { + for (i=0; i<ilimit; i+=2) + { + int32_t G_ds = 0, B_ds = 0, R_ds = 0; + uint8_t y0, y1, u, v; + + pixels = inputRGB[i]; + temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) ); + y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; + + G_ds += (pixels>>1) & 0x03E0; + B_ds += (pixels<<5) & 0x03E0; + R_ds += (pixels>>6) & 0x03E0; + + pixels = inputRGB[i+1]; + temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) ); + y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; + + G_ds += (pixels>>1) & 0x03E0; + B_ds += (pixels<<5) & 0x03E0; + R_ds += (pixels>>6) & 0x03E0; + + R_ds >>= 1; + B_ds >>= 1; + G_ds >>= 1; + + tmp = R_ds - B_ds; + + u = cb_tab[(((B_ds-G_ds)<<SHIFT2) - GAMMA*tmp)>>(SHIFT2+2)]; + v = cr_tab[(((R_ds-G_ds)<<SHIFT2) + DELTA*tmp)>>(SHIFT2+2)]; + + tempY[0] = y0; + tempY[1] = y1; + tempU[0] = u; + tempV[0] = v; + + tempY += 2; + tempU += 2; + tempV += 2; + } + + inputRGB += pitch_src; + } + + return 1; +} + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height) +{ + if (!tables_initialized) { + initYtab(); + initCrtab(); + initCbtab(); + tables_initialized = 1; + } + + uint32_t param[6]; + param[0] = (uint32_t) width; + param[1] = (uint32_t) height; + param[2] = (uint32_t) width; + param[3] = (uint32_t) height; + param[4] = (uint32_t) width; + param[5] = (uint32_t) 0; + + uint8_t *table[3]; + table[0] = gYTable; + table[1] = gCbTable + 384; + table[2] = gCrTable + 384; + + ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table); +} + +const int FakeCamera::kRed; +const int FakeCamera::kGreen; +const int FakeCamera::kBlue; + +FakeCamera::FakeCamera(int width, int height) + : mTmpRgb16Buffer(0) +{ + setSize(width, height); +} + +FakeCamera::~FakeCamera() +{ + delete[] mTmpRgb16Buffer; +} + +void FakeCamera::setSize(int width, int height) +{ + mWidth = width; + mHeight = height; + mCounter = 0; + mCheckX = 0; + mCheckY = 0; + + // This will cause it to be reallocated on the next call + // to getNextFrameAsYuv422(). + delete[] mTmpRgb16Buffer; + mTmpRgb16Buffer = 0; +} + +void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer) +{ + int size = mWidth / 10; + + drawCheckerboard(buffer, size); + + int x = ((mCounter*3)&255); + if(x>128) x = 255 - x; + int y = ((mCounter*5)&255); + if(y>128) y = 255 - y; + + drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue); + + mCounter++; +} + +void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer) +{ + if (mTmpRgb16Buffer == 0) + mTmpRgb16Buffer = new uint16_t[mWidth * mHeight]; + + getNextFrameAsRgb565(mTmpRgb16Buffer); + convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); +} + +void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow) +{ + int square_xstop, square_ystop, shadow_xstop, shadow_ystop; + + square_xstop = min(mWidth, x+size); + square_ystop = min(mHeight, y+size); + shadow_xstop = min(mWidth, x+size+(size/4)); + shadow_ystop = min(mHeight, y+size+(size/4)); + + // Do the shadow. + uint16_t *sh = &dst[(y+(size/4))*mWidth]; + for (int j = y + (size/4); j < shadow_ystop; j++) { + for (int i = x + (size/4); i < shadow_xstop; i++) { + sh[i] &= shadow; + } + sh += mWidth; + } + + // Draw the square. + uint16_t *sq = &dst[y*mWidth]; + for (int j = y; j < square_ystop; j++) { + for (int i = x; i < square_xstop; i++) { + sq[i] = color; + } + sq += mWidth; + } +} + +void FakeCamera::drawCheckerboard(uint16_t *dst, int size) +{ + bool black = true; + + if((mCheckX/size)&1) + black = false; + if((mCheckY/size)&1) + black = !black; + + int county = mCheckY%size; + int checkxremainder = mCheckX%size; + + for(int y=0;y<mHeight;y++) { + int countx = checkxremainder; + bool current = black; + for(int x=0;x<mWidth;x++) { + dst[y*mWidth+x] = current?0:0xffff; + if(countx++ >= size) { + countx=0; + current = !current; + } + } + if(county++ >= size) { + county=0; + black = !black; + } + } + mCheckX += 3; + mCheckY++; +} + + +void FakeCamera::dump(int fd) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY); + result.append(buffer); + ::write(fd, result.string(), result.size()); +} + + +}; // namespace android diff --git a/services/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h new file mode 100644 index 0000000..f7f8803 --- /dev/null +++ b/services/camera/libcameraservice/FakeCamera.h @@ -0,0 +1,67 @@ +/* +** +** Copyright 2008, 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_FAKECAMERA_H +#define ANDROID_HARDWARE_FAKECAMERA_H + +#include <sys/types.h> +#include <stdint.h> + +namespace android { + +/* + * FakeCamera is used in the CameraHardwareStub to provide a fake video feed + * when the system does not have a camera in hardware. + * The fake video is a moving black and white checkerboard background with a + * bouncing gray square in the foreground. + * This class is not thread-safe. + * + * TODO: Since the major methods provides a raw/uncompressed video feed, rename + * this class to RawVideoSource. + */ + +class FakeCamera { +public: + FakeCamera(int width, int height); + ~FakeCamera(); + + void setSize(int width, int height); + void getNextFrameAsYuv422(uint8_t *buffer); + // Write to the fd a string representing the current state. + void dump(int fd) const; + +private: + // TODO: remove the uint16_t buffer param everywhere since it is a field of + // this class. + void getNextFrameAsRgb565(uint16_t *buffer); + + void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow); + void drawCheckerboard(uint16_t *buffer, int size); + + static const int kRed = 0xf800; + static const int kGreen = 0x07c0; + static const int kBlue = 0x003e; + + int mWidth, mHeight; + int mCounter; + int mCheckX, mCheckY; + uint16_t *mTmpRgb16Buffer; +}; + +}; // namespace android + +#endif // ANDROID_HARDWARE_FAKECAMERA_H diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk new file mode 100644 index 0000000..9bb190a --- /dev/null +++ b/services/camera/tests/CameraServiceTest/Android.mk @@ -0,0 +1,24 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= CameraServiceTest.cpp + +LOCAL_MODULE:= CameraServiceTest + +LOCAL_MODULE_TAGS := tests + +LOCAL_C_INCLUDES += \ + frameworks/base/libs + +LOCAL_CFLAGS := + +LOCAL_SHARED_LIBRARIES += \ + libbinder \ + libcutils \ + libutils \ + libui \ + libcamera_client \ + libsurfaceflinger_client + +include $(BUILD_EXECUTABLE) diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp new file mode 100644 index 0000000..9fc795b --- /dev/null +++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp @@ -0,0 +1,849 @@ +#define LOG_TAG "CameraServiceTest" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <surfaceflinger/ISurface.h> +#include <camera/Camera.h> +#include <camera/CameraParameters.h> +#include <ui/GraphicBuffer.h> +#include <camera/ICamera.h> +#include <camera/ICameraClient.h> +#include <camera/ICameraService.h> +#include <ui/Overlay.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +using namespace android; + +// +// Assertion and Logging utilities +// +#define INFO(...) \ + do { \ + printf(__VA_ARGS__); \ + printf("\n"); \ + LOGD(__VA_ARGS__); \ + } while(0) + +void assert_fail(const char *file, int line, const char *func, const char *expr) { + INFO("assertion failed at file %s, line %d, function %s:", + file, line, func); + INFO("%s", expr); + exit(1); +} + +void assert_eq_fail(const char *file, int line, const char *func, + const char *expr, int actual) { + INFO("assertion failed at file %s, line %d, function %s:", + file, line, func); + INFO("(expected) %s != (actual) %d", expr, actual); + exit(1); +} + +#define ASSERT(e) \ + do { \ + if (!(e)) \ + assert_fail(__FILE__, __LINE__, __func__, #e); \ + } while(0) + +#define ASSERT_EQ(expected, actual) \ + do { \ + int _x = (actual); \ + if (_x != (expected)) \ + assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \ + } while(0) + +// +// Holder service for pass objects between processes. +// +class IHolder : public IInterface { +protected: + enum { + HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION, + HOLDER_GET, + HOLDER_CLEAR + }; +public: + DECLARE_META_INTERFACE(Holder); + + virtual void put(sp<IBinder> obj) = 0; + virtual sp<IBinder> get() = 0; + virtual void clear() = 0; +}; + +class BnHolder : public BnInterface<IHolder> { + virtual status_t onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +class BpHolder : public BpInterface<IHolder> { +public: + BpHolder(const sp<IBinder>& impl) + : BpInterface<IHolder>(impl) { + } + + virtual void put(sp<IBinder> obj) { + Parcel data, reply; + data.writeStrongBinder(obj); + remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual sp<IBinder> get() { + Parcel data, reply; + remote()->transact(HOLDER_GET, data, &reply); + return reply.readStrongBinder(); + } + + virtual void clear() { + Parcel data, reply; + remote()->transact(HOLDER_CLEAR, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder"); + +status_t BnHolder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case HOLDER_PUT: { + put(data.readStrongBinder()); + return NO_ERROR; + } break; + case HOLDER_GET: { + reply->writeStrongBinder(get()); + return NO_ERROR; + } break; + case HOLDER_CLEAR: { + clear(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +class HolderService : public BnHolder { + virtual void put(sp<IBinder> obj) { + mObj = obj; + } + virtual sp<IBinder> get() { + return mObj; + } + virtual void clear() { + mObj.clear(); + } +private: + sp<IBinder> mObj; +}; + +// +// A mock CameraClient +// +class MCameraClient : public BnCameraClient { +public: + virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); + virtual void dataCallbackTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& data) {} + + // new functions + void clearStat(); + enum OP { EQ, GE, LE, GT, LT }; + void assertNotify(int32_t msgType, OP op, int count); + void assertData(int32_t msgType, OP op, int count); + void waitNotify(int32_t msgType, OP op, int count); + void waitData(int32_t msgType, OP op, int count); + void assertDataSize(int32_t msgType, OP op, int dataSize); + + void setReleaser(ICamera *releaser) { + mReleaser = releaser; + } +private: + Mutex mLock; + Condition mCond; + DefaultKeyedVector<int32_t, int> mNotifyCount; + DefaultKeyedVector<int32_t, int> mDataCount; + DefaultKeyedVector<int32_t, int> mDataSize; + bool test(OP op, int v1, int v2); + + ICamera *mReleaser; +}; + +void MCameraClient::clearStat() { + Mutex::Autolock _l(mLock); + mNotifyCount.clear(); + mDataCount.clear(); + mDataSize.clear(); +} + +bool MCameraClient::test(OP op, int v1, int v2) { + switch (op) { + case EQ: return v1 == v2; + case GT: return v1 > v2; + case LT: return v1 < v2; + case GE: return v1 >= v2; + case LE: return v1 <= v2; + default: ASSERT(0); break; + } + return false; +} + +void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { + Mutex::Autolock _l(mLock); + int v = mNotifyCount.valueFor(msgType); + ASSERT(test(op, v, count)); +} + +void MCameraClient::assertData(int32_t msgType, OP op, int count) { + Mutex::Autolock _l(mLock); + int v = mDataCount.valueFor(msgType); + ASSERT(test(op, v, count)); +} + +void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { + Mutex::Autolock _l(mLock); + int v = mDataSize.valueFor(msgType); + ASSERT(test(op, v, dataSize)); +} + +void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { + INFO(__func__); + Mutex::Autolock _l(mLock); + ssize_t i = mNotifyCount.indexOfKey(msgType); + if (i < 0) { + mNotifyCount.add(msgType, 1); + } else { + ++mNotifyCount.editValueAt(i); + } + mCond.signal(); +} + +void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { + INFO(__func__); + int dataSize = data->size(); + INFO("data type = %d, size = %d", msgType, dataSize); + Mutex::Autolock _l(mLock); + ssize_t i = mDataCount.indexOfKey(msgType); + if (i < 0) { + mDataCount.add(msgType, 1); + mDataSize.add(msgType, dataSize); + } else { + ++mDataCount.editValueAt(i); + mDataSize.editValueAt(i) = dataSize; + } + mCond.signal(); + + if (msgType == CAMERA_MSG_VIDEO_FRAME) { + ASSERT(mReleaser != NULL); + mReleaser->releaseRecordingFrame(data); + } +} + +void MCameraClient::waitNotify(int32_t msgType, OP op, int count) { + INFO("waitNotify: %d, %d, %d", msgType, op, count); + Mutex::Autolock _l(mLock); + while (true) { + int v = mNotifyCount.valueFor(msgType); + if (test(op, v, count)) { + break; + } + mCond.wait(mLock); + } +} + +void MCameraClient::waitData(int32_t msgType, OP op, int count) { + INFO("waitData: %d, %d, %d", msgType, op, count); + Mutex::Autolock _l(mLock); + while (true) { + int v = mDataCount.valueFor(msgType); + if (test(op, v, count)) { + break; + } + mCond.wait(mLock); + } +} + +// +// A mock Surface +// +class MSurface : public BnSurface { +public: + virtual status_t registerBuffers(const BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); + + // new functions + void clearStat(); + void waitUntil(int c0, int c1, int c2); + +private: + // check callback count + Condition mCond; + Mutex mLock; + int registerBuffersCount; + int postBufferCount; + int unregisterBuffersCount; +}; + +status_t MSurface::registerBuffers(const BufferHeap& buffers) { + INFO(__func__); + Mutex::Autolock _l(mLock); + ++registerBuffersCount; + mCond.signal(); + return NO_ERROR; +} + +void MSurface::postBuffer(ssize_t offset) { + // INFO(__func__); + Mutex::Autolock _l(mLock); + ++postBufferCount; + mCond.signal(); +} + +void MSurface::unregisterBuffers() { + INFO(__func__); + Mutex::Autolock _l(mLock); + ++unregisterBuffersCount; + mCond.signal(); +} + +sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { + INFO(__func__); + return NULL; +} + +void MSurface::clearStat() { + Mutex::Autolock _l(mLock); + registerBuffersCount = 0; + postBufferCount = 0; + unregisterBuffersCount = 0; +} + +void MSurface::waitUntil(int c0, int c1, int c2) { + INFO("waitUntil: %d %d %d", c0, c1, c2); + Mutex::Autolock _l(mLock); + while (true) { + if (registerBuffersCount >= c0 && + postBufferCount >= c1 && + unregisterBuffersCount >= c2) { + break; + } + mCond.wait(mLock); + } +} + +sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format, + int32_t orientation) { + // We don't expect this to be called in current hardware. + ASSERT(0); + sp<OverlayRef> dummy; + return dummy; +} + +// +// Utilities to use the Holder service +// +sp<IHolder> getHolder() { + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT(sm != 0); + sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder")); + ASSERT(binder != 0); + sp<IHolder> holder = interface_cast<IHolder>(binder); + ASSERT(holder != 0); + return holder; +} + +void putTempObject(sp<IBinder> obj) { + INFO(__func__); + getHolder()->put(obj); +} + +sp<IBinder> getTempObject() { + INFO(__func__); + return getHolder()->get(); +} + +void clearTempObject() { + INFO(__func__); + getHolder()->clear(); +} + +// +// Get a Camera Service +// +sp<ICameraService> getCameraService() { + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT(sm != 0); + sp<IBinder> binder = sm->getService(String16("media.camera")); + ASSERT(binder != 0); + sp<ICameraService> cs = interface_cast<ICameraService>(binder); + ASSERT(cs != 0); + return cs; +} + +// +// Various Connect Tests +// +void testConnect() { + INFO(__func__); + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc); + ASSERT(c != 0); + c->disconnect(); +} + +void testAllowConnectOnceOnly() { + INFO(__func__); + sp<ICameraService> cs = getCameraService(); + // Connect the first client. + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc); + ASSERT(c != 0); + // Same client -- ok. + ASSERT(cs->connect(cc) != 0); + // Different client -- not ok. + sp<MCameraClient> cc2 = new MCameraClient(); + ASSERT(cs->connect(cc2) == 0); + c->disconnect(); +} + +void testReconnectFailed() { + INFO(__func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + sp<MCameraClient> cc2 = new MCameraClient(); + ASSERT(c->connect(cc2) != NO_ERROR); +} + +void testReconnectSuccess() { + INFO(__func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + sp<MCameraClient> cc = new MCameraClient(); + ASSERT(c->connect(cc) == NO_ERROR); +} + +void testLockFailed() { + INFO(__func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() != NO_ERROR); +} + +void testLockUnlockSuccess() { + INFO(__func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() == NO_ERROR); + ASSERT(c->unlock() == NO_ERROR); +} + +void testLockSuccess() { + INFO(__func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() == NO_ERROR); +} + +// +// Run the connect tests in another process. +// +const char *gExecutable; + +struct FunctionTableEntry { + const char *name; + void (*func)(); +}; + +FunctionTableEntry function_table[] = { +#define ENTRY(x) {#x, &x} + ENTRY(testReconnectFailed), + ENTRY(testReconnectSuccess), + ENTRY(testLockUnlockSuccess), + ENTRY(testLockFailed), + ENTRY(testLockSuccess), +#undef ENTRY +}; + +void runFunction(const char *tag) { + INFO("runFunction: %s", tag); + int entries = sizeof(function_table) / sizeof(function_table[0]); + for (int i = 0; i < entries; i++) { + if (strcmp(function_table[i].name, tag) == 0) { + (*function_table[i].func)(); + return; + } + } + ASSERT(0); +} + +void runInAnotherProcess(const char *tag) { + pid_t pid = fork(); + if (pid == 0) { + execlp(gExecutable, gExecutable, tag, NULL); + ASSERT(0); + } else { + int status; + ASSERT_EQ(pid, wait(&status)); + ASSERT_EQ(0, status); + } +} + +void testReconnect() { + INFO(__func__); + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc); + ASSERT(c != 0); + // Reconnect to the same client -- ok. + ASSERT(c->connect(cc) == NO_ERROR); + // Reconnect to a different client (but the same pid) -- ok. + sp<MCameraClient> cc2 = new MCameraClient(); + ASSERT(c->connect(cc2) == NO_ERROR); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); +} + +void testLockUnlock() { + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc); + ASSERT(c != 0); + // We can lock as many times as we want. + ASSERT(c->lock() == NO_ERROR); + ASSERT(c->lock() == NO_ERROR); + // Lock from a different process -- not ok. + putTempObject(c->asBinder()); + runInAnotherProcess("testLockFailed"); + // Unlock then lock from a different process -- ok. + ASSERT(c->unlock() == NO_ERROR); + runInAnotherProcess("testLockUnlockSuccess"); + // Unlock then lock from a different process -- ok. + runInAnotherProcess("testLockSuccess"); + c->disconnect(); + clearTempObject(); +} + +void testReconnectFromAnotherProcess() { + INFO(__func__); + + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc); + ASSERT(c != 0); + // Reconnect from a different process -- not ok. + putTempObject(c->asBinder()); + runInAnotherProcess("testReconnectFailed"); + // Unlock then reconnect from a different process -- ok. + ASSERT(c->unlock() == NO_ERROR); + runInAnotherProcess("testReconnectSuccess"); + c->disconnect(); + clearTempObject(); +} + +// We need to flush the command buffer after the reference +// to ICamera is gone. The sleep is for the server to run +// the destructor for it. +static void flushCommands() { + IPCThreadState::self()->flushCommands(); + usleep(200000); // 200ms +} + +// Run a test case +#define RUN(class_name) do { \ + { \ + INFO(#class_name); \ + class_name instance; \ + instance.run(); \ + } \ + flushCommands(); \ +} while(0) + +// Base test case after the the camera is connected. +class AfterConnect { +protected: + sp<ICameraService> cs; + sp<MCameraClient> cc; + sp<ICamera> c; + + AfterConnect() { + cs = getCameraService(); + cc = new MCameraClient(); + c = cs->connect(cc); + ASSERT(c != 0); + } + + ~AfterConnect() { + c.clear(); + cc.clear(); + cs.clear(); + } +}; + +class TestSetPreviewDisplay : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStartPreview : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + + surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer + surface->clearStat(); + + c->disconnect(); + // TODO: CameraService crashes for this. Fix it. +#if 0 + sp<MSurface> another_surface = new MSurface(); + c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers + // is called. + surface->waitUntil(0, 0, 1); // needs unregisterBuffers +#endif + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStartPreviewWithoutDisplay : AfterConnect { +public: + void run() { + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +// Base test case after the the camera is connected and the preview is started. +class AfterStartPreview : public AfterConnect { +protected: + sp<MSurface> surface; + + AfterStartPreview() { + surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + ASSERT(c->startPreview() == NO_ERROR); + } + + ~AfterStartPreview() { + surface.clear(); + } +}; + +class TestAutoFocus : public AfterStartPreview { +public: + void run() { + cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0); + c->autoFocus(); + cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStopPreview : public AfterStartPreview { +public: + void run() { + ASSERT(c->previewEnabled() == true); + c->stopPreview(); + ASSERT(c->previewEnabled() == false); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestTakePicture: public AfterStartPreview { +public: + void run() { + ASSERT(c->takePicture() == NO_ERROR); + cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + c->stopPreview(); +#if 1 // TODO: It crashes if we don't have this. Fix it. + usleep(100000); +#endif + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestTakeMultiplePictures: public AfterStartPreview { +public: + void run() { + for (int i = 0; i < 10; i++) { + cc->clearStat(); + ASSERT(c->takePicture() == NO_ERROR); + cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + usleep(100000); // 100ms + } + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestGetParameters: public AfterStartPreview { +public: + void run() { + String8 param_str = c->getParameters(); + INFO(param_str); + } +}; + +class TestPictureSize : public AfterStartPreview { +public: + void checkOnePicture(int w, int h) { + const float rate = 0.5; // byte per pixel limit + int pixels = w * h; + + CameraParameters param(c->getParameters()); + param.setPictureSize(w, h); + c->setParameters(param.flatten()); + + cc->clearStat(); + ASSERT(c->takePicture() == NO_ERROR); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT, + int(pixels * rate)); + cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + usleep(100000); // 100ms + } + + void run() { + checkOnePicture(2048, 1536); + checkOnePicture(1600, 1200); + checkOnePicture(1024, 768); + } +}; + +class TestPreviewCallbackFlag : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + + // Try all flag combinations. + for (int v = 0; v < 8; v++) { + cc->clearStat(); + c->setPreviewCallbackFlag(v); + ASSERT(c->previewEnabled() == false); + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + sleep(2); + c->stopPreview(); + if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0); + } else { + if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10); + } else { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1); + } + } + } + } +}; + +class TestRecording : public AfterConnect { +public: + void run() { + ASSERT(c->recordingEnabled() == false); + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK); + cc->setReleaser(c.get()); + c->startRecording(); + ASSERT(c->recordingEnabled() == true); + sleep(2); + c->stopRecording(); + cc->setReleaser(NULL); + cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10); + } +}; + +class TestPreviewSize : public AfterStartPreview { +public: + void checkOnePicture(int w, int h) { + int size = w*h*3/2; // should read from parameters + + c->stopPreview(); + + CameraParameters param(c->getParameters()); + param.setPreviewSize(w, h); + c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK); + c->setParameters(param.flatten()); + + c->startPreview(); + + cc->clearStat(); + cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1); + cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size); + } + + void run() { + checkOnePicture(480, 320); + checkOnePicture(352, 288); + checkOnePicture(176, 144); + } +}; + +void runHolderService() { + defaultServiceManager()->addService( + String16("CameraServiceTest.Holder"), new HolderService()); + ProcessState::self()->startThreadPool(); +} + +int main(int argc, char **argv) +{ + if (argc != 1) { + runFunction(argv[1]); + return 0; + } + INFO("CameraServiceTest start"); + gExecutable = argv[0]; + runHolderService(); + + testConnect(); flushCommands(); + testAllowConnectOnceOnly(); flushCommands(); + testReconnect(); flushCommands(); + testLockUnlock(); flushCommands(); + testReconnectFromAnotherProcess(); flushCommands(); + + RUN(TestSetPreviewDisplay); + RUN(TestStartPreview); + RUN(TestStartPreviewWithoutDisplay); + RUN(TestAutoFocus); + RUN(TestStopPreview); + RUN(TestTakePicture); + RUN(TestTakeMultiplePictures); + RUN(TestGetParameters); + RUN(TestPictureSize); + RUN(TestPreviewCallbackFlag); + RUN(TestRecording); + RUN(TestPreviewSize); +} diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk new file mode 100644 index 0000000..86eb78d --- /dev/null +++ b/services/surfaceflinger/Android.mk @@ -0,0 +1,51 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + clz.cpp.arm \ + DisplayHardware/DisplayHardware.cpp \ + DisplayHardware/DisplayHardwareBase.cpp \ + BlurFilter.cpp.arm \ + Layer.cpp \ + LayerBase.cpp \ + LayerBuffer.cpp \ + LayerBlur.cpp \ + LayerDim.cpp \ + MessageQueue.cpp \ + SurfaceFlinger.cpp \ + Tokenizer.cpp \ + Transform.cpp + +LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +ifeq ($(TARGET_BOARD_PLATFORM), msm7k) + LOCAL_CFLAGS += -DDIM_WITH_TEXTURE +endif + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger \ + libhardware \ + libutils \ + libEGL \ + libGLESv1_CM \ + libbinder \ + libui \ + libsurfaceflinger_client + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) + +LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc + +LOCAL_MODULE:= libsurfaceflinger + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h new file mode 100644 index 0000000..e2bcf6a --- /dev/null +++ b/services/surfaceflinger/Barrier.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 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_BARRIER_H +#define ANDROID_BARRIER_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> + +namespace android { + +class Barrier +{ +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + void open() { + // gcc memory barrier, this makes sure all memory writes + // have been issued by gcc. On an SMP system we'd need a real + // h/w barrier. + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +}; // namespace android + +#endif // ANDROID_BARRIER_H diff --git a/services/surfaceflinger/BlurFilter.cpp b/services/surfaceflinger/BlurFilter.cpp new file mode 100644 index 0000000..1ffbd5b --- /dev/null +++ b/services/surfaceflinger/BlurFilter.cpp @@ -0,0 +1,376 @@ +/* +** +** Copyright 2006, 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <utils/Errors.h> + +#include <pixelflinger/pixelflinger.h> + +#include "clz.h" + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +#if BYTE_ORDER == LITTLE_ENDIAN +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return v; +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return v; +} +#else +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +#endif + +const int BLUR_DITHER_BITS = 6; // dither weights stored on 6 bits +const int BLUR_DITHER_ORDER_SHIFT= 3; +const int BLUR_DITHER_ORDER = (1<<BLUR_DITHER_ORDER_SHIFT); +const int BLUR_DITHER_SIZE = BLUR_DITHER_ORDER * BLUR_DITHER_ORDER; +const int BLUR_DITHER_MASK = BLUR_DITHER_ORDER-1; + +static const uint8_t gDitherMatrix[BLUR_DITHER_SIZE] = { + 0, 32, 8, 40, 2, 34, 10, 42, + 48, 16, 56, 24, 50, 18, 58, 26, + 12, 44, 4, 36, 14, 46, 6, 38, + 60, 28, 52, 20, 62, 30, 54, 22, + 3, 35, 11, 43, 1, 33, 9, 41, + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +}; + + +template <int FACTOR = 0> +struct BlurColor565 +{ + typedef uint16_t type; + int r, g, b; + inline BlurColor565() { } + inline BlurColor565(uint16_t v) { + r = v >> 11; + g = (v >> 5) & 0x3E; + b = v & 0x1F; + } + inline void clear() { r=g=b=0; } + inline uint16_t to(int shift, int last, int dither) const { + int R = r; + int G = g; + int B = b; + if (UNLIKELY(last)) { + if (FACTOR>0) { + int L = (R+G+B)>>1; + R += (((L>>1) - R) * FACTOR) >> 8; + G += (((L ) - G) * FACTOR) >> 8; + B += (((L>>1) - B) * FACTOR) >> 8; + } + R += (dither << shift) >> BLUR_DITHER_BITS; + G += (dither << shift) >> BLUR_DITHER_BITS; + B += (dither << shift) >> BLUR_DITHER_BITS; + } + R >>= shift; + G >>= shift; + B >>= shift; + return (R<<11) | (G<<5) | B; + } + inline BlurColor565& operator += (const BlurColor565& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor565& operator -= (const BlurColor565& rhs) { + r -= rhs.r; + g -= rhs.g; + b -= rhs.b; + return *this; + } +}; + +template <int FACTOR = 0> +struct BlurColor888X +{ + typedef uint32_t type; + int r, g, b; + inline BlurColor888X() { } + inline BlurColor888X(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + r = v & 0xFF; + g = (v >> 8) & 0xFF; + b = (v >> 16) & 0xFF; + } + inline void clear() { r=g=b=0; } + inline uint32_t to(int shift, int last, int dither) const { + int R = r; + int G = g; + int B = b; + if (UNLIKELY(last)) { + if (FACTOR>0) { + int L = (R+G+G+B)>>2; + R += ((L - R) * FACTOR) >> 8; + G += ((L - G) * FACTOR) >> 8; + B += ((L - B) * FACTOR) >> 8; + } + } + R >>= shift; + G >>= shift; + B >>= shift; + return BLUR_HOST_TO_RGBA((0xFF<<24) | (B<<16) | (G<<8) | R); + } + inline BlurColor888X& operator += (const BlurColor888X& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor888X& operator -= (const BlurColor888X& rhs) { + r -= rhs.r; + g -= rhs.g; + b -= rhs.b; + return *this; + } +}; + +struct BlurGray565 +{ + typedef uint16_t type; + int l; + inline BlurGray565() { } + inline BlurGray565(uint16_t v) { + int r = v >> 11; + int g = (v >> 5) & 0x3F; + int b = v & 0x1F; + l = (r + g + b + 1)>>1; + } + inline void clear() { l=0; } + inline uint16_t to(int shift, int last, int dither) const { + int L = l; + if (UNLIKELY(last)) { + L += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= shift; + return ((L>>1)<<11) | (L<<5) | (L>>1); + } + inline BlurGray565& operator += (const BlurGray565& rhs) { + l += rhs.l; + return *this; + } + inline BlurGray565& operator -= (const BlurGray565& rhs) { + l -= rhs.l; + return *this; + } +}; + +struct BlurGray8888 +{ + typedef uint32_t type; + int l, a; + inline BlurGray8888() { } + inline BlurGray8888(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + int r = v & 0xFF; + int g = (v >> 8) & 0xFF; + int b = (v >> 16) & 0xFF; + a = v >> 24; + l = r + g + g + b; + } + inline void clear() { l=a=0; } + inline uint32_t to(int shift, int last, int dither) const { + int L = l; + int A = a; + if (UNLIKELY(last)) { + L += (dither << (shift+2)) >> BLUR_DITHER_BITS; + A += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= (shift+2); + A >>= shift; + return BLUR_HOST_TO_RGBA((A<<24) | (L<<16) | (L<<8) | L); + } + inline BlurGray8888& operator += (const BlurGray8888& rhs) { + l += rhs.l; + a += rhs.a; + return *this; + } + inline BlurGray8888& operator -= (const BlurGray8888& rhs) { + l -= rhs.l; + a -= rhs.a; + return *this; + } +}; + + +template<typename PIXEL> +static status_t blurFilter( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat) +{ + typedef typename PIXEL::type TYPE; + + const int shift = 31 - clz(kernelSizeUser); + const int areaShift = shift*2; + const int kernelSize = 1<<shift; + const int kernelHalfSize = kernelSize/2; + const int mask = kernelSize-1; + const int w = src->width; + const int h = src->height; + const uint8_t* ditherMatrix = gDitherMatrix; + + // we need a temporary buffer to store one line of blurred columns + // as well as kernelSize lines of source pixels organized as a ring buffer. + void* const temporary_buffer = malloc( + (w + kernelSize) * sizeof(PIXEL) + + (src->stride * kernelSize) * sizeof(TYPE)); + if (!temporary_buffer) + return NO_MEMORY; + + PIXEL* const sums = (PIXEL*)temporary_buffer; + TYPE* const scratch = (TYPE*)(sums + w + kernelSize); + + // Apply the blur 'repeat' times, this is used to approximate + // gaussian blurs. 3 times gives good results. + for (int k=0 ; k<repeat ; k++) { + + // Clear the columns sums for this round + memset(sums, 0, (w + kernelSize) * sizeof(PIXEL)); + TYPE* head; + TYPE pixel; + PIXEL current; + + // Since we're going to override the source data we need + // to copy it in a temporary buffer. Only kernelSize lines are + // required. But since we start in the center of the kernel, + // we only copy half of the data, and fill the rest with zeros + // (assuming black/transparent pixels). + memcpy( scratch + src->stride*kernelHalfSize, + src->data, + src->stride*kernelHalfSize*sizeof(TYPE)); + + // sum half of each column, because we assume the first half is + // zeros (black/transparent). + for (int y=0 ; y<kernelHalfSize ; y++) { + head = (TYPE*)src->data + y*src->stride; + for (int x=0 ; x<w ; x++) + sums[x] += PIXEL( *head++ ); + } + + for (int y=0 ; y<h ; y++) { + TYPE* fb = (TYPE*)dst->data + y*dst->stride; + + // compute the dither matrix line + uint8_t const * ditherY = ditherMatrix + + (y & BLUR_DITHER_MASK)*BLUR_DITHER_ORDER; + + // Horizontal blur pass on the columns sums + int count, dither, x=0; + PIXEL const * out= sums; + PIXEL const * in = sums; + current.clear(); + + count = kernelHalfSize; + do { + current += *in; + in++; + } while (--count); + + count = kernelHalfSize; + do { + current += *in; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++; + } while (--count); + + count = w-kernelSize; + do { + current += *in; + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++, out++; + } while (--count); + + count = kernelHalfSize; + do { + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + out++; + } while (--count); + + // vertical blur pass, subtract the oldest line from each columns + // and add a new line. Subtract or add zeros at the top + // and bottom edges. + TYPE* const tail = scratch + (y & mask) * src->stride; + if (y >= kernelHalfSize) { + for (int x=0 ; x<w ; x++) + sums[x] -= PIXEL( tail[x] ); + } + if (y < h-kernelSize) { + memcpy( tail, + (TYPE*)src->data + (y+kernelHalfSize)*src->stride, + src->stride*sizeof(TYPE)); + for (int x=0 ; x<w ; x++) + sums[x] += PIXEL( tail[x] ); + } + } + + // The subsequent passes are always done in-place. + src = dst; + } + + free(temporary_buffer); + + return NO_ERROR; +} + +template status_t blurFilter< BlurColor565<0x80> >( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat); + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat) +{ + status_t err = BAD_VALUE; + if (image->format == GGL_PIXEL_FORMAT_RGB_565) { + err = blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); + } else if (image->format == GGL_PIXEL_FORMAT_RGBX_8888) { + err = blurFilter< BlurColor888X<0x80> >(image, image, kernelSizeUser, repeat); + } + return err; +} + +} // namespace android + +//err = blur< BlurColor565<0x80> >(dst, src, kernelSizeUser, repeat); +//err = blur<BlurGray565>(dst, src, kernelSizeUser, repeat); +//err = blur<BlurGray8888>(dst, src, kernelSizeUser, repeat); diff --git a/services/surfaceflinger/BlurFilter.h b/services/surfaceflinger/BlurFilter.h new file mode 100644 index 0000000..294db43 --- /dev/null +++ b/services/surfaceflinger/BlurFilter.h @@ -0,0 +1,35 @@ +/* +** +** Copyright 2006, 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_BLUR_FILTER_H +#define ANDROID_BLUR_FILTER_H + +#include <stdint.h> +#include <utils/Errors.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat); + +} // namespace android + +#endif // ANDROID_BLUR_FILTER_H diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp new file mode 100644 index 0000000..ea68352 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include <cutils/properties.h> + +#include <utils/RefBase.h> +#include <utils/Log.h> + +#include <ui/PixelFormat.h> +#include <ui/FramebufferNativeWindow.h> +#include <ui/EGLUtils.h> + +#include <GLES/gl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pixelflinger/pixelflinger.h> + +#include "DisplayHardware/DisplayHardware.h" + +#include <hardware/copybit.h> +#include <hardware/overlay.h> +#include <hardware/gralloc.h> + +using namespace android; + + +static __attribute__((noinline)) +void checkGLErrors() +{ + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) + break; + LOGE("GL error 0x%04x", int(error)); + } while(true); +} + +static __attribute__((noinline)) +void checkEGLErrors(const char* token) +{ + EGLint error = eglGetError(); + if (error && error != EGL_SUCCESS) { + LOGE("%s: EGL error 0x%04x (%s)", + token, int(error), EGLUtils::strerror(error)); + } +} + +/* + * Initialize the display to the specified values. + * + */ + +DisplayHardware::DisplayHardware( + const sp<SurfaceFlinger>& flinger, + uint32_t dpy) + : DisplayHardwareBase(flinger, dpy) +{ + init(dpy); +} + +DisplayHardware::~DisplayHardware() +{ + fini(); +} + +float DisplayHardware::getDpiX() const { return mDpiX; } +float DisplayHardware::getDpiY() const { return mDpiY; } +float DisplayHardware::getDensity() const { return mDensity; } +float DisplayHardware::getRefreshRate() const { return mRefreshRate; } +int DisplayHardware::getWidth() const { return mWidth; } +int DisplayHardware::getHeight() const { return mHeight; } +PixelFormat DisplayHardware::getFormat() const { return mFormat; } +uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; } +uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; } + +void DisplayHardware::init(uint32_t dpy) +{ + mNativeWindow = new FramebufferNativeWindow(); + framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); + + mOverlayEngine = NULL; + hw_module_t const* module; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } + + // initialize EGL + EGLint attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE, 0, + EGL_NONE + }; + + // debug: disable h/w rendering + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.sf.hw", property, NULL) > 0) { + if (atoi(property) == 0) { + LOGW("H/W composition disabled"); + attribs[2] = EGL_CONFIG_CAVEAT; + attribs[3] = EGL_SLOW_CONFIG; + } + } + + EGLint w, h, dummy; + EGLint numConfigs=0; + EGLSurface surface; + EGLContext context; + mFlags = CACHED_BUFFERS; + + // TODO: all the extensions below should be queried through + // eglGetProcAddress(). + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, NULL, NULL); + eglGetConfigs(display, NULL, 0, &numConfigs); + + EGLConfig config; + status_t err = EGLUtils::selectConfigForNativeWindow( + display, attribs, mNativeWindow.get(), &config); + LOGE_IF(err, "couldn't find an EGLConfig matching the screen format"); + + EGLint r,g,b,a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + + /* + * Gather EGL extensions + */ + + const char* const egl_extensions = eglQueryString( + display, EGL_EXTENSIONS); + + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + LOGI("version : %s", eglQueryString(display, EGL_VERSION)); + LOGI("extensions: %s", egl_extensions); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + + + if (mNativeWindow->isUpdateOnDemand()) { + mFlags |= PARTIAL_UPDATES; + } + + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { + if (dummy == EGL_SLOW_CONFIG) + mFlags |= SLOW_CONFIG; + } + + /* + * Create our main surface + */ + + surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + + if (mFlags & PARTIAL_UPDATES) { + // if we have partial updates, we definitely don't need to + // preserve the backbuffer, which may be costly. + eglSurfaceAttrib(display, surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + } + + if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { + if (dummy == EGL_BUFFER_PRESERVED) { + mFlags |= BUFFER_PRESERVED; + } + } + + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); + +#ifdef EGL_ANDROID_swap_rectangle + if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) { + if (eglSetSwapRectangleANDROID(display, surface, + 0, 0, mWidth, mHeight) == EGL_TRUE) { + // This could fail if this extension is not supported by this + // specific surface (of config) + mFlags |= SWAP_RECTANGLE; + } + } + // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE + // choose PARTIAL_UPDATES, which should be more efficient + if (mFlags & PARTIAL_UPDATES) + mFlags &= ~SWAP_RECTANGLE; +#endif + + + LOGI("flags : %08x", mFlags); + + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; + + /* Read density from build-specific ro.sf.lcd_density property + * except if it is overridden by qemu.sf.lcd_density. + */ + if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) { + if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { + LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); + strcpy(property, "160"); + } + } else { + /* for the emulator case, reset the dpi values too */ + mDpiX = mDpiY = atoi(property); + } + mDensity = atoi(property) * (1.0f/160.0f); + + + /* + * Create our OpenGL ES context + */ + + context = eglCreateContext(display, config, NULL, NULL); + + /* + * Gather OpenGL ES extensions + */ + + eglMakeCurrent(display, surface, surface, context); + const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); + const char* const gl_renderer = (const char*)glGetString(GL_RENDERER); + LOGI("OpenGL informations:"); + LOGI("vendor : %s", glGetString(GL_VENDOR)); + LOGI("renderer : %s", gl_renderer); + LOGI("version : %s", glGetString(GL_VERSION)); + LOGI("extensions: %s", gl_extensions); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); + LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); + LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + +#if 0 + // for drivers that don't have proper support for flushing cached buffers + // on gralloc unlock, uncomment this block and test for the specific + // renderer substring + if (strstr(gl_renderer, "<some vendor string>")) { + LOGD("Assuming uncached graphics buffers."); + mFlags &= ~CACHED_BUFFERS; + } +#endif + + if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { + mFlags |= NPOT_EXTENSION; + } + if (strstr(gl_extensions, "GL_OES_draw_texture")) { + mFlags |= DRAW_TEXTURE_EXTENSION; + } +#ifdef EGL_ANDROID_image_native_buffer + if (strstr( gl_extensions, "GL_OES_EGL_image") && + (strstr(egl_extensions, "EGL_KHR_image_base") || + strstr(egl_extensions, "EGL_KHR_image")) && + strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) { + mFlags |= DIRECT_TEXTURE; + } +#else +#warning "EGL_ANDROID_image_native_buffer not supported" +#endif + + + // Unbind the context from this thread + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = fbDev->format; + mPageFlipCount = 0; +} + +/* + * Clean up. Throw out our local state. + * + * (It's entirely possible we'll never get here, since this is meant + * for real hardware, which doesn't restart.) + */ + +void DisplayHardware::fini() +{ + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mDisplay); + overlay_control_close(mOverlayEngine); +} + +void DisplayHardware::releaseScreen() const +{ + DisplayHardwareBase::releaseScreen(); +} + +void DisplayHardware::acquireScreen() const +{ + DisplayHardwareBase::acquireScreen(); +} + +uint32_t DisplayHardware::getPageFlipCount() const { + return mPageFlipCount; +} + +status_t DisplayHardware::compositionComplete() const { + return mNativeWindow->compositionComplete(); +} + +void DisplayHardware::flip(const Region& dirty) const +{ + checkGLErrors(); + + EGLDisplay dpy = mDisplay; + EGLSurface surface = mSurface; + +#ifdef EGL_ANDROID_swap_rectangle + if (mFlags & SWAP_RECTANGLE) { + const Region newDirty(dirty.intersect(bounds())); + const Rect b(newDirty.getBounds()); + eglSetSwapRectangleANDROID(dpy, surface, + b.left, b.top, b.width(), b.height()); + } +#endif + + if (mFlags & PARTIAL_UPDATES) { + mNativeWindow->setUpdateRectangle(dirty.getBounds()); + } + + mPageFlipCount++; + eglSwapBuffers(dpy, surface); + checkEGLErrors("eglSwapBuffers"); + + // for debugging + //glClearColor(1,0,0,0); + //glClear(GL_COLOR_BUFFER_BIT); +} + +uint32_t DisplayHardware::getFlags() const +{ + return mFlags; +} + +void DisplayHardware::makeCurrent() const +{ + eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); +} diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h new file mode 100644 index 0000000..df046af --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 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_DISPLAY_HARDWARE_H +#define ANDROID_DISPLAY_HARDWARE_H + +#include <stdlib.h> + +#include <ui/PixelFormat.h> +#include <ui/Region.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pixelflinger/pixelflinger.h> + +#include "DisplayHardware/DisplayHardwareBase.h" + +struct overlay_control_device_t; +struct framebuffer_device_t; +struct copybit_image_t; + +namespace android { + +class FramebufferNativeWindow; + +class DisplayHardware : public DisplayHardwareBase +{ +public: + enum { + DIRECT_TEXTURE = 0x00000002, + COPY_BITS_EXTENSION = 0x00000008, + NPOT_EXTENSION = 0x00000100, + DRAW_TEXTURE_EXTENSION = 0x00000200, + BUFFER_PRESERVED = 0x00010000, + PARTIAL_UPDATES = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, + CACHED_BUFFERS = 0x00100000 + }; + + DisplayHardware( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); + + ~DisplayHardware(); + + void releaseScreen() const; + void acquireScreen() const; + + // Flip the front and back buffers if the back buffer is "dirty". Might + // be instantaneous, might involve copying the frame buffer around. + void flip(const Region& dirty) const; + + float getDpiX() const; + float getDpiY() const; + float getRefreshRate() const; + float getDensity() const; + int getWidth() const; + int getHeight() const; + PixelFormat getFormat() const; + uint32_t getFlags() const; + void makeCurrent() const; + uint32_t getMaxTextureSize() const; + uint32_t getMaxViewportDims() const; + + uint32_t getPageFlipCount() const; + EGLDisplay getEGLDisplay() const { return mDisplay; } + overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } + + status_t compositionComplete() const; + + Rect bounds() const { + return Rect(mWidth, mHeight); + } + +private: + void init(uint32_t displayIndex) __attribute__((noinline)); + void fini() __attribute__((noinline)); + + EGLDisplay mDisplay; + EGLSurface mSurface; + EGLContext mContext; + EGLConfig mConfig; + float mDpiX; + float mDpiY; + float mRefreshRate; + float mDensity; + int mWidth; + int mHeight; + PixelFormat mFormat; + uint32_t mFlags; + mutable uint32_t mPageFlipCount; + GLint mMaxViewportDims; + GLint mMaxTextureSize; + + sp<FramebufferNativeWindow> mNativeWindow; + overlay_control_device_t* mOverlayEngine; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_H diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp new file mode 100644 index 0000000..1d09f84 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 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. + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/resource.h> + +#include <linux/unistd.h> + +#include <utils/Log.h> + +#include "DisplayHardware/DisplayHardwareBase.h" +#include "SurfaceFlinger.h" + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- +namespace android { + +static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; +static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; +static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep"; +static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake"; + +// This dir exists if the framebuffer console is present, either built into +// the kernel or loaded as a module. +static char const * const kFbconSysDir = "/sys/class/graphics/fbcon"; + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase( + const sp<SurfaceFlinger>& flinger) + : Thread(false), mFlinger(flinger) { +} + +DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() { +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThread::DisplayEventThread( + const sp<SurfaceFlinger>& flinger) + : DisplayEventThreadBase(flinger) +{ +} + +DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() +{ +} + +bool DisplayHardwareBase::DisplayEventThread::threadLoop() +{ + int err = 0; + char buf; + int fd; + + fd = open(kSleepFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + if (err >= 0) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) { + mBarrier.close(); + flinger->screenReleased(0); + mBarrier.wait(); + } + } + fd = open(kWakeFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + if (err >= 0) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + return true; +} + +status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const +{ + mBarrier.open(); + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::readyToRun() +{ + if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) { + if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) { + LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); + return NO_INIT; + } + kSleepFileName = kOldSleepFileName; + kWakeFileName = kOldWakeFileName; + } + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::initCheck() const +{ + return (((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0) || + (access(kOldSleepFileName, R_OK) == 0 && + access(kOldWakeFileName, R_OK) == 0)) && + access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0; + +DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread( + const sp<SurfaceFlinger>& flinger) + : DisplayEventThreadBase(flinger), consoleFd(-1) +{ + sSignalCatcherPid = 0; + + // create a new console + char const * const ttydev = "/dev/tty0"; + int fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open %s", ttydev); + this->consoleFd = -errno; + return; + } + + // to make sure that we are in text mode + int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT); + if (res<0) { + LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + } + + // get the current console + struct vt_stat vs; + res = ioctl(fd, VT_GETSTATE, &vs); + if (res<0) { + LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + this->consoleFd = -errno; + return; + } + + // switch to console 7 (which is what X normaly uses) + int vtnum = 7; + do { + res = ioctl(fd, VT_ACTIVATE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d", + fd, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d", + fd, res, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + // open the new console + close(fd); + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open new console %s", ttydev); + this->consoleFd = -errno; + return; + } + + /* disable console line buffer, echo, ... */ + struct termios ttyarg; + ioctl(fd, TCGETS , &ttyarg); + ttyarg.c_iflag = 0; + ttyarg.c_lflag = 0; + ioctl(fd, TCSETS , &ttyarg); + + // set up signals so we're notified when the console changes + // we can't use SIGUSR1 because it's used by the java-vm + vm.mode = VT_PROCESS; + vm.waitv = 0; + vm.relsig = SIGUSR2; + vm.acqsig = SIGUNUSED; + vm.frsig = 0; + + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.relsig, &act, NULL); + + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.acqsig, &act, NULL); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + // switch to graphic mode + res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS); + LOGW_IF(res<0, + "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res); + + this->prev_vt_num = vs.v_active; + this->vt_num = vtnum; + this->consoleFd = fd; +} + +DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread() +{ + if (this->consoleFd >= 0) { + int fd = this->consoleFd; + int prev_vt_num = this->prev_vt_num; + int res; + ioctl(fd, KDSETMODE, (void*)KD_TEXT); + do { + res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + close(fd); + char const * const ttydev = "/dev/tty0"; + fd = open(ttydev, O_RDWR | O_SYNC); + ioctl(fd, VT_DISALLOCATE, 0); + close(fd); + } +} + +status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun() +{ + if (this->consoleFd >= 0) { + sSignalCatcherPid = gettid(); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + int res = ioctl(this->consoleFd, VT_SETMODE, &vm); + if (res<0) { + LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)", + this->consoleFd, errno, strerror(errno)); + } + return NO_ERROR; + } + return this->consoleFd; +} + +void DisplayHardwareBase::ConsoleManagerThread::requestExit() +{ + Thread::requestExit(); + if (sSignalCatcherPid != 0) { + // wake the thread up + kill(sSignalCatcherPid, SIGINT); + // wait for it... + } +} + +void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig) +{ + // resend the signal to our signal catcher thread + LOGW("received signal %d in thread %d, resending to %d", + sig, gettid(), sSignalCatcherPid); + + // we absolutely need the delays below because without them + // our main thread never gets a chance to handle the signal. + usleep(10000); + kill(sSignalCatcherPid, sig); + usleep(10000); +} + +status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const +{ + int fd = this->consoleFd; + int err = ioctl(fd, VT_RELDISP, (void*)1); + LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)", + fd, errno, strerror(errno)); + return (err<0) ? (-errno) : status_t(NO_ERROR); +} + +bool DisplayHardwareBase::ConsoleManagerThread::threadLoop() +{ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + + int sig = 0; + sigwait(&mask, &sig); + + if (sig == vm.relsig) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + //LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenReleased(0); + } else if (sig == vm.acqsig) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + //LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + + return true; +} + +status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const +{ + return consoleFd >= 0 ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex) + : mCanDraw(true) +{ + mDisplayEventThread = new DisplayEventThread(flinger); + if (mDisplayEventThread->initCheck() != NO_ERROR) { + // fall-back on the console + mDisplayEventThread = new ConsoleManagerThread(flinger); + } +} + +DisplayHardwareBase::~DisplayHardwareBase() +{ + // request exit + mDisplayEventThread->requestExitAndWait(); +} + + +bool DisplayHardwareBase::canDraw() const +{ + return mCanDraw; +} + +void DisplayHardwareBase::releaseScreen() const +{ + status_t err = mDisplayEventThread->releaseScreen(); + if (err >= 0) { + //LOGD("screen given-up"); + mCanDraw = false; + } +} + +void DisplayHardwareBase::acquireScreen() const +{ + status_t err = mDisplayEventThread->acquireScreen(); + if (err >= 0) { + //LOGD("screen returned"); + mCanDraw = true; + } +} + +}; // namespace android diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h new file mode 100644 index 0000000..8369bb8 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 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_DISPLAY_HARDWARE_BASE_H +#define ANDROID_DISPLAY_HARDWARE_BASE_H + +#include <stdint.h> +#include <utils/RefBase.h> +#include <utils/threads.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include "Barrier.h" + +namespace android { + +class SurfaceFlinger; + +class DisplayHardwareBase +{ +public: + DisplayHardwareBase( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); + + ~DisplayHardwareBase(); + + // console managment + void releaseScreen() const; + void acquireScreen() const; + bool canDraw() const; + +private: + class DisplayEventThreadBase : public Thread { + protected: + wp<SurfaceFlinger> mFlinger; + public: + DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger); + virtual ~DisplayEventThreadBase(); + virtual void onFirstRef() { + run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); + } + virtual status_t acquireScreen() const { return NO_ERROR; }; + virtual status_t releaseScreen() const { return NO_ERROR; }; + virtual status_t initCheck() const = 0; + }; + + class DisplayEventThread : public DisplayEventThreadBase + { + mutable Barrier mBarrier; + public: + DisplayEventThread(const sp<SurfaceFlinger>& flinger); + virtual ~DisplayEventThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + class ConsoleManagerThread : public DisplayEventThreadBase + { + int consoleFd; + int vt_num; + int prev_vt_num; + vt_mode vm; + static void sigHandler(int sig); + static pid_t sSignalCatcherPid; + public: + ConsoleManagerThread(const sp<SurfaceFlinger>& flinger); + virtual ~ConsoleManagerThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void requestExit(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + sp<DisplayEventThreadBase> mDisplayEventThread; + mutable int mCanDraw; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_BASE_H diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp new file mode 100644 index 0000000..ce7e9aa --- /dev/null +++ b/services/surfaceflinger/Layer.cpp @@ -0,0 +1,630 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/properties.h> +#include <cutils/native_handle.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> + +#include <surfaceflinger/Surface.h> + +#include "clz.h" +#include "Layer.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + + +#define DEBUG_RESIZE 0 + + +namespace android { + +template <typename T> inline T min(T a, T b) { + return a<b ? a : b; +} + +// --------------------------------------------------------------------------- + +const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; +const char* const Layer::typeID = "Layer"; + +// --------------------------------------------------------------------------- + +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& c, int32_t i) + : LayerBaseClient(flinger, display, c, i), + mSecure(false), + mNoEGLImageForSwBuffers(false), + mNeedsBlending(true), + mNeedsDithering(false) +{ + // no OpenGL operation is possible here, since we might not be + // in the OpenGL thread. + mFrontBufferIndex = lcblk->getFrontBuffer(); +} + +Layer::~Layer() +{ + destroy(); + // the actual buffers will be destroyed here +} + +void Layer::destroy() +{ + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + if (mTextures[i].name != -1U) { + glDeleteTextures(1, &mTextures[i].name); + mTextures[i].name = -1U; + } + if (mTextures[i].image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTextures[i].image); + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + Mutex::Autolock _l(mLock); + mBuffers[i].clear(); + mWidth = mHeight = 0; + } + mSurface.clear(); +} + +sp<LayerBaseClient::Surface> Layer::createSurface() const +{ + return mSurface; +} + +status_t Layer::ditch() +{ + // the layer is not on screen anymore. free as much resources as possible + mFreezeLock.clear(); + destroy(); + return NO_ERROR; +} + +status_t Layer::setBuffers( uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags) +{ + // this surfaces pixel format + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err) return err; + + // the display's pixel format + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + uint32_t const maxSurfaceDims = min( + hw.getMaxTextureSize(), hw.getMaxViewportDims()); + + // never allow a surface larger than what our underlying GL implementation + // can handle. + if ((uint32_t(w)>maxSurfaceDims) || (uint32_t(h)>maxSurfaceDims)) { + return BAD_VALUE; + } + + PixelFormatInfo displayInfo; + getPixelFormatInfo(hw.getFormat(), &displayInfo); + const uint32_t hwFlags = hw.getFlags(); + + mFormat = format; + mWidth = w; + mHeight = h; + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; + mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); + + // we use the red index + int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); + int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); + mNeedsDithering = layerRedsize > displayRedSize; + + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mBuffers[i] = new GraphicBuffer(); + } + mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); + return NO_ERROR; +} + +void Layer::reloadTexture(const Region& dirty) +{ + Mutex::Autolock _l(mLock); + sp<GraphicBuffer> buffer(getFrontBufferLocked()); + if (buffer == NULL) { + // this situation can happen if we ran out of memory for instance. + // not much we can do. continue to use whatever texture was bound + // to this context. + return; + } + + const int index = mFrontBufferIndex; + + // create the new texture name if needed + if (UNLIKELY(mTextures[index].name == -1U)) { + mTextures[index].name = createTexture(); + mTextures[index].width = 0; + mTextures[index].height = 0; + } + +#ifdef EGL_ANDROID_image_native_buffer + if (mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { + if (mTextures[index].dirty) { + if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + goto slowpath; + } + } + } else { + if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || + mHybridBuffer->height != buffer->height)) { + mHybridBuffer.clear(); + mHybridBuffer = new GraphicBuffer( + buffer->width, buffer->height, buffer->format, + GraphicBuffer::USAGE_SW_WRITE_OFTEN | + GraphicBuffer::USAGE_HW_TEXTURE); + if (initializeEglImage( + mHybridBuffer, &mTextures[0]) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + mHybridBuffer.clear(); + goto slowpath; + } + } + + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + Texture* const texture(&mTextures[0]); + + glBindTexture(GL_TEXTURE_2D, texture->name); + + sp<GraphicBuffer> buf(mHybridBuffer); + void* vaddr; + res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); + if (res == NO_ERROR) { + int bpp = 0; + switch (t.format) { + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + bpp = 4; + break; + default: + if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + bpp = 1; + break; + } + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, texture->name, t.format); + } + if (bpp) { + const Rect bounds(dirty.getBounds()); + size_t src_stride = t.stride; + size_t dst_stride = buf->stride; + if (src_stride == dst_stride && + bounds.width() == t.width && + bounds.height() == t.height) + { + memcpy(vaddr, t.data, t.height * t.stride * bpp); + } else { + GLubyte const * src = t.data + + (bounds.left + bounds.top * src_stride) * bpp; + GLubyte * dst = (GLubyte *)vaddr + + (bounds.left + bounds.top * dst_stride) * bpp; + const size_t length = bounds.width() * bpp; + size_t h = bounds.height(); + src_stride *= bpp; + dst_stride *= bpp; + while (h--) { + memcpy(dst, src, length); + dst += dst_stride; + src += src_stride; + } + } + } + buf->unlock(); + } + buffer->unlock(); + } + } + } else +#endif + { +slowpath: + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + loadTexture(&mTextures[0], dirty, t); + buffer->unlock(); + } + } +} + +void Layer::onDraw(const Region& clip) const +{ + int index = mFrontBufferIndex; + if (mTextures[index].image == EGL_NO_IMAGE_KHR) + index = 0; + GLuint textureName = mTextures[index].name; + if (UNLIKELY(textureName == -1LU)) { + // the texture has not been created yet, this Layer has + // in fact never been drawn into. This happens frequently with + // SurfaceView because the WindowManager can't know when the client + // has drawn the first time. + + // If there is nothing under us, we paint the screen in black, otherwise + // we just skip this update. + + // figure out if there is something below us + Region under; + const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer(drawingLayers[i]); + if (layer.get() == static_cast<LayerBase const*>(this)) + break; + under.orSelf(layer->visibleRegionScreen); + } + // if not everything below us is covered, we plug the holes! + Region holes(clip.subtract(under)); + if (!holes.isEmpty()) { + clearWithOpenGL(holes); + } + return; + } + drawWithOpenGL(clip, mTextures[index]); +} + +sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + + // this ensures our client doesn't go away while we're accessing + // the shared area. + sp<Client> ourClient(client.promote()); + if (ourClient == 0) { + // oops, the client is already gone + return buffer; + } + + /* + * This is called from the client's Surface::dequeue(). This can happen + * at any time, especially while we're in the middle of using the + * buffer 'index' as our front buffer. + * + * Make sure the buffer we're resizing is not the front buffer and has been + * dequeued. Once this condition is asserted, we are guaranteed that this + * buffer cannot become the front buffer under our feet, since we're called + * from Surface::dequeue() + */ + status_t err = lcblk->assertReallocate(index); + LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err)); + if (err != NO_ERROR) { + // the surface may have died + return buffer; + } + + uint32_t w, h; + { // scope for the lock + Mutex::Autolock _l(mLock); + w = mWidth; + h = mHeight; + buffer = mBuffers[index]; + + // destroy() could have been called before we get here, we log it + // because it's uncommon, and the code below should handle it + LOGW_IF(buffer==0, + "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", + index, w, h); + + mBuffers[index].clear(); + } + + const uint32_t effectiveUsage = getEffectiveUsage(usage); + if (buffer!=0 && buffer->getStrongCount() == 1) { + err = buffer->reallocate(w, h, mFormat, effectiveUsage); + } else { + // here we have to reallocate a new buffer because we could have a + // client in our process with a reference to it (eg: status bar), + // and we can't release the handle under its feet. + buffer.clear(); + buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage); + err = buffer->initCheck(); + } + + if (err || buffer->handle == 0) { + LOGE_IF(err || buffer->handle == 0, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)", + this, index, w, h, strerror(-err)); + } else { + LOGD_IF(DEBUG_RESIZE, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d, handle=%p", + this, index, w, h, buffer->handle); + } + + if (err == NO_ERROR && buffer->handle != 0) { + Mutex::Autolock _l(mLock); + if (mWidth && mHeight) { + // and we have new buffer + mBuffers[index] = buffer; + // texture is now dirty... + mTextures[index].dirty = true; + } else { + // oops we got killed while we were allocating the buffer + buffer.clear(); + } + } + return buffer; +} + +uint32_t Layer::getEffectiveUsage(uint32_t usage) const +{ + /* + * buffers used for software rendering, but h/w composition + * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE + * + * buffers used for h/w rendering and h/w composition + * are allocated with HW_RENDER | HW_TEXTURE + * + * buffers used with h/w rendering and either NPOT or no egl_image_ext + * are allocated with SW_READ_RARELY | HW_RENDER + * + */ + + if (mSecure) { + // secure buffer, don't store it into the GPU + usage = GraphicBuffer::USAGE_SW_READ_OFTEN | + GraphicBuffer::USAGE_SW_WRITE_OFTEN; + } else { + // it's allowed to modify the usage flags here, but generally + // the requested flags should be honored. + if (mNoEGLImageForSwBuffers) { + if (usage & GraphicBuffer::USAGE_HW_MASK) { + // request EGLImage for h/w buffers only + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + } else { + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + } + return usage; +} + +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if ((front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h)) { + // the size changed, we need to ask our client to request a new buffer + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), requested (%dx%d), " + "drawing (%d,%d), (%dx%d), (%dx%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h), + int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), + int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); + + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } + } + + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; + + // record the new size, form this point on, when the client request a + // buffer, it'll get the new size. + setDrawingSize(temp.requested_w, temp.requested_h); + + // all buffers need reallocation + lcblk->reallocate(); + } + + if (temp.sequence != front.sequence) { + if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) { + // this surface is now hidden, so it shouldn't hold a freeze lock + // (it may never redraw, which is fine if it is hidden) + mFreezeLock.clear(); + } + } + + return LayerBase::doTransaction(flags); +} + +void Layer::setDrawingSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + mWidth = w; + mHeight = h; +} + +// ---------------------------------------------------------------------------- +// pageflip handling... +// ---------------------------------------------------------------------------- + +void Layer::lockPageFlip(bool& recomputeVisibleRegions) +{ + ssize_t buf = lcblk->retireAndLock(); + if (buf < NO_ERROR) { + //LOGW("nothing to retire (%s)", strerror(-buf)); + // NOTE: here the buffer is locked because we will used + // for composition later in the loop + return; + } + + // ouch, this really should never happen + if (uint32_t(buf)>=NUM_BUFFERS) { + LOGE("retireAndLock() buffer index (%d) out of range", buf); + mPostedDirtyRegion.clear(); + return; + } + + // we retired a buffer, which becomes the new front buffer + mFrontBufferIndex = buf; + + // get the dirty region + sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); + if (newFrontBuffer != NULL) { + // compute the posted region + const Region dirty(lcblk->getDirtyRegion(buf)); + mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); + + // update the layer size and release freeze-lock + const Layer::State& front(drawingState()); + if (newFrontBuffer->getWidth() == front.requested_w && + newFrontBuffer->getHeight() == front.requested_h) + { + if ((front.w != front.requested_w) || + (front.h != front.requested_h)) + { + // Here we pretend the transaction happened by updating the + // current and drawing states. Drawing state is only accessed + // in this thread, no need to have it locked + Layer::State& editDraw(mDrawingState); + editDraw.w = editDraw.requested_w; + editDraw.h = editDraw.requested_h; + + // We also need to update the current state so that we don't + // end-up doing too much work during the next transaction. + // NOTE: We actually don't need hold the transaction lock here + // because State::w and State::h are only accessed from + // this thread + Layer::State& editTemp(currentState()); + editTemp.w = editDraw.w; + editTemp.h = editDraw.h; + + // recompute visible region + recomputeVisibleRegions = true; + } + + // we now have the correct size, unfreeze the screen + mFreezeLock.clear(); + } + } else { + // this should not happen unless we ran out of memory while + // allocating the buffer. we're hoping that things will get back + // to normal the next time the app tries to draw into this buffer. + // meanwhile, pretend the screen didn't update. + mPostedDirtyRegion.clear(); + } + + if (lcblk->getQueuedCount()) { + // signal an event if we have more buffers waiting + mFlinger->signalEvent(); + } + + if (!mPostedDirtyRegion.isEmpty()) { + reloadTexture( mPostedDirtyRegion ); + } +} + +void Layer::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + Region dirtyRegion(mPostedDirtyRegion); + if (!dirtyRegion.isEmpty()) { + mPostedDirtyRegion.clear(); + // The dirty region is given in the layer's coordinate space + // transform the dirty region by the surface's transformation + // and the global transformation. + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + dirtyRegion = tr.transform(dirtyRegion); + + // At this point, the dirty region is in screen space. + // Make sure it's constrained by the visible region (which + // is in screen space as well). + dirtyRegion.andSelf(visibleRegionScreen); + outDirtyRegion.orSelf(dirtyRegion); + } + if (visibleRegionScreen.isEmpty()) { + // an invisible layer should not hold a freeze-lock + // (because it may never be updated and thereore never release it) + mFreezeLock.clear(); + } +} + +void Layer::finishPageFlip() +{ + status_t err = lcblk->unlock( mFrontBufferIndex ); + LOGE_IF(err!=NO_ERROR, + "layer %p, buffer=%d wasn't locked!", + this, mFrontBufferIndex); +} + +// --------------------------------------------------------------------------- + +Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner) + : Surface(flinger, id, owner->getIdentity(), owner) +{ +} + +Layer::SurfaceLayer::~SurfaceLayer() +{ +} + +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + sp<Layer> owner(getOwner()); + if (owner != 0) { + LOGE_IF(uint32_t(index)>=NUM_BUFFERS, + "getBuffer() index (%d) out of range", index); + if (uint32_t(index) < NUM_BUFFERS) { + buffer = owner->requestBuffer(index, usage); + } + } + return buffer; +} + +// --------------------------------------------------------------------------- + + +}; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h new file mode 100644 index 0000000..743afb4 --- /dev/null +++ b/services/surfaceflinger/Layer.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 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_LAYER_H +#define ANDROID_LAYER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> +#include <pixelflinger/pixelflinger.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "LayerBase.h" +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class FreezeLock; + +// --------------------------------------------------------------------------- + +const size_t NUM_BUFFERS = 2; + +class Layer : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + + virtual ~Layer(); + + status_t setBuffers(uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); + + void setDrawingSize(uint32_t w, uint32_t h); + + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t transactionFlags); + virtual void lockPageFlip(bool& recomputeVisibleRegions); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual void finishPageFlip(); + virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool needsDithering() const { return mNeedsDithering; } + virtual bool isSecure() const { return mSecure; } + virtual sp<Surface> createSurface() const; + virtual status_t ditch(); + + // only for debugging + inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } + // only for debugging + inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + // only for debugging + inline PixelFormat pixelFormat() const { return mFormat; } + // only for debugging + inline int getFrontBufferIndex() const { return mFrontBufferIndex; } + +private: + inline sp<GraphicBuffer> getFrontBufferLocked() { + return mBuffers[mFrontBufferIndex]; + } + + void reloadTexture(const Region& dirty); + + uint32_t getEffectiveUsage(uint32_t usage) const; + + sp<GraphicBuffer> requestBuffer(int index, int usage); + void destroy(); + + class SurfaceLayer : public LayerBaseClient::Surface { + public: + SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner); + ~SurfaceLayer(); + private: + virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + sp<Layer> getOwner() const { + return static_cast<Layer*>(Surface::getOwner().get()); + } + }; + friend class SurfaceLayer; + + sp<Surface> mSurface; + + bool mSecure; + bool mNoEGLImageForSwBuffers; + int32_t mFrontBufferIndex; + bool mNeedsBlending; + bool mNeedsDithering; + Region mPostedDirtyRegion; + sp<FreezeLock> mFreezeLock; + PixelFormat mFormat; + + // protected by mLock + sp<GraphicBuffer> mBuffers[NUM_BUFFERS]; + Texture mTextures[NUM_BUFFERS]; + sp<GraphicBuffer> mHybridBuffer; + uint32_t mWidth; + uint32_t mHeight; + + mutable Mutex mLock; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_H diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp new file mode 100644 index 0000000..a8b735e --- /dev/null +++ b/services/surfaceflinger/LayerBase.cpp @@ -0,0 +1,829 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "LayerBase.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBase::typeInfo = 1; +const char* const LayerBase::typeID = "LayerBase"; + +const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; +const char* const LayerBaseClient::typeID = "LayerBaseClient"; + +// --------------------------------------------------------------------------- + +LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) + : dpy(display), contentDirty(false), + mFlinger(flinger), + mTransformed(false), + mUseLinearFiltering(false), + mOrientation(0), + mLeft(0), mTop(0), + mTransactionFlags(0), + mPremultipliedAlpha(true), mDebug(false), + mInvalidate(0) +{ + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + mFlags = hw.getFlags(); +} + +LayerBase::~LayerBase() +{ +} + +void LayerBase::setName(const String8& name) { + mName = name; +} + +String8 LayerBase::getName() const { + return mName; +} + +const GraphicPlane& LayerBase::graphicPlane(int dpy) const +{ + return mFlinger->graphicPlane(dpy); +} + +GraphicPlane& LayerBase::graphicPlane(int dpy) +{ + return mFlinger->graphicPlane(dpy); +} + +void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags) +{ + uint32_t layerFlags = 0; + if (flags & ISurfaceComposer::eHidden) + layerFlags = ISurfaceComposer::eLayerHidden; + + if (flags & ISurfaceComposer::eNonPremultiplied) + mPremultipliedAlpha = false; + + mCurrentState.z = 0; + mCurrentState.w = w; + mCurrentState.h = h; + mCurrentState.requested_w = w; + mCurrentState.requested_h = h; + mCurrentState.alpha = 0xFF; + mCurrentState.flags = layerFlags; + mCurrentState.sequence = 0; + mCurrentState.transform.set(0, 0); + + // drawing state & current state are identical + mDrawingState = mCurrentState; +} + +void LayerBase::commitTransaction() { + mDrawingState = mCurrentState; +} +void LayerBase::forceVisibilityTransaction() { + // this can be called without SurfaceFlinger.mStateLock, but if we + // can atomically increment the sequence number, it doesn't matter. + android_atomic_inc(&mCurrentState.sequence); + requestTransaction(); +} +bool LayerBase::requestTransaction() { + int32_t old = setTransactionFlags(eTransactionNeeded); + return ((old & eTransactionNeeded) == 0); +} +uint32_t LayerBase::getTransactionFlags(uint32_t flags) { + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} +uint32_t LayerBase::setTransactionFlags(uint32_t flags) { + return android_atomic_or(flags, &mTransactionFlags); +} + +bool LayerBase::setPosition(int32_t x, int32_t y) { + if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) + return false; + mCurrentState.sequence++; + mCurrentState.transform.set(x, y); + requestTransaction(); + return true; +} +bool LayerBase::setLayer(uint32_t z) { + if (mCurrentState.z == z) + return false; + mCurrentState.sequence++; + mCurrentState.z = z; + requestTransaction(); + return true; +} +bool LayerBase::setSize(uint32_t w, uint32_t h) { + if (mCurrentState.requested_w == w && mCurrentState.requested_h == h) + return false; + mCurrentState.requested_w = w; + mCurrentState.requested_h = h; + requestTransaction(); + return true; +} +bool LayerBase::setAlpha(uint8_t alpha) { + if (mCurrentState.alpha == alpha) + return false; + mCurrentState.sequence++; + mCurrentState.alpha = alpha; + requestTransaction(); + return true; +} +bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { + // TODO: check the matrix has changed + mCurrentState.sequence++; + mCurrentState.transform.set( + matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); + requestTransaction(); + return true; +} +bool LayerBase::setTransparentRegionHint(const Region& transparent) { + // TODO: check the region has changed + mCurrentState.sequence++; + mCurrentState.transparentRegion = transparent; + requestTransaction(); + return true; +} +bool LayerBase::setFlags(uint8_t flags, uint8_t mask) { + const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); + if (mCurrentState.flags == newFlags) + return false; + mCurrentState.sequence++; + mCurrentState.flags = newFlags; + requestTransaction(); + return true; +} + +Rect LayerBase::visibleBounds() const +{ + return mTransformedBounds; +} + +void LayerBase::setVisibleRegion(const Region& visibleRegion) { + // always called from main thread + visibleRegionScreen = visibleRegion; +} + +void LayerBase::setCoveredRegion(const Region& coveredRegion) { + // always called from main thread + coveredRegionScreen = coveredRegion; +} + +uint32_t LayerBase::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if ((front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h)) { + // resize the layer, set the physical size to the requested size + Layer::State& editTemp(currentState()); + editTemp.w = temp.requested_w; + editTemp.h = temp.requested_h; + } + + if ((front.w != temp.w) || (front.h != temp.h)) { + // invalidate and recompute the visible regions if needed + flags |= Layer::eVisibleRegion; + } + + if (temp.sequence != front.sequence) { + // invalidate and recompute the visible regions if needed + flags |= eVisibleRegion; + this->contentDirty = true; + + const bool linearFiltering = mUseLinearFiltering; + mUseLinearFiltering = false; + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // we may use linear filtering, if the matrix scales us + const uint8_t type = temp.transform.getType(); + if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) { + mUseLinearFiltering = true; + } + } + } + + // Commit the transaction + commitTransaction(); + return flags; +} + +void LayerBase::validateVisibility(const Transform& planeTransform) +{ + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + const bool transformed = tr.transformed(); + + uint32_t w = s.w; + uint32_t h = s.h; + tr.transform(mVertices[0], 0, 0); + tr.transform(mVertices[1], 0, h); + tr.transform(mVertices[2], w, h); + tr.transform(mVertices[3], w, 0); + if (UNLIKELY(transformed)) { + // NOTE: here we could also punt if we have too many rectangles + // in the transparent region + if (tr.preserveRects()) { + // transform the transparent region + transparentRegionScreen = tr.transform(s.transparentRegion); + } else { + // transformation too complex, can't do the transparent region + // optimization. + transparentRegionScreen.clear(); + } + } else { + transparentRegionScreen = s.transparentRegion; + } + + // cache a few things... + mOrientation = tr.getOrientation(); + mTransformedBounds = tr.makeBounds(w, h); + mTransformed = transformed; + mLeft = tr.tx(); + mTop = tr.ty(); +} + +void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) +{ +} + +void LayerBase::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { + outDirtyRegion.orSelf(visibleRegionScreen); + } +} + +void LayerBase::finishPageFlip() +{ +} + +void LayerBase::invalidate() +{ + if ((android_atomic_or(1, &mInvalidate)&1) == 0) { + mFlinger->signalEvent(); + } +} + +void LayerBase::drawRegion(const Region& reg) const +{ + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + if (it != end) { + Rect r; + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t fbWidth = hw.getWidth(); + const int32_t fbHeight = hw.getHeight(); + const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, + { fbWidth, fbHeight }, { 0, fbHeight } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::draw(const Region& inClip) const +{ + // invalidate the region we'll update + Region clip(inClip); // copy-on-write, so no-op most of the time + + // Remove the transparent area from the clipping region + const State& s = drawingState(); + if (LIKELY(!s.transparentRegion.isEmpty())) { + clip.subtract(transparentRegionScreen); + if (clip.isEmpty()) { + // usually this won't happen because this should be taken care of + // by SurfaceFlinger::computeVisibleRegions() + return; + } + } + + // reset GL state + glEnable(GL_SCISSOR_TEST); + + onDraw(clip); + + /* + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0x8000, 0, 0x10000); + drawRegion(transparentRegionScreen); + glDisable(GL_BLEND); + */ +} + +GLuint LayerBase::createTexture() const +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + return textureName; +} + +void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, + GLclampx green, GLclampx blue, + GLclampx alpha) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + glColor4x(red,green,blue,alpha); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + glEnable(GL_SCISSOR_TEST); + glVertexPointer(2, GL_FIXED, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void LayerBase::clearWithOpenGL(const Region& clip) const +{ + clearWithOpenGL(clip,0,0,0,0); +} + +void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + const State& s(drawingState()); + + // bind our texture + validateTexture(texture.name); + uint32_t width = texture.width; + uint32_t height = texture.height; + + glEnable(GL_TEXTURE_2D); + + if (UNLIKELY(s.alpha < 0xFF)) { + // We have an alpha-modulation. We need to modulate all + // texture components by alpha because we're always using + // premultiplied alpha. + + // If the texture doesn't have an alpha channel we can + // use REPLACE and switch to non premultiplied alpha + // blending (SRCA/ONE_MINUS_SRCA). + + GLenum env, src; + if (needsBlending()) { + env = GL_MODULATE; + src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + } else { + env = GL_REPLACE; + src = GL_SRC_ALPHA; + } + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(alpha, alpha, alpha, alpha); + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); + } else { + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + if (needsBlending()) { + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + } + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + + //StopWatch watch("GL transformed"); + const GLfixed texCoords[4][2] = { + { 0, 0 }, + { 0, 0x10000 }, + { 0x10000, 0x10000 }, + { 0x10000, 0 } + }; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + // the texture's source is rotated + switch (texture.transform) { + case HAL_TRANSFORM_ROT_90: + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + break; + case HAL_TRANSFORM_ROT_180: + glTranslatef(1, 1, 0); + glRotatef(-180, 0, 0, 1); + break; + case HAL_TRANSFORM_ROT_270: + glTranslatef(1, 0, 0); + glRotatef(-270, 0, 0, 1); + break; + } + + if (texture.NPOTAdjust) { + glScalef(texture.wScale, texture.hScale, 1.0f); + } + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, texCoords); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void LayerBase::validateTexture(GLint textureName) const +{ + glBindTexture(GL_TEXTURE_2D, textureName); + // TODO: reload the texture if needed + // this is currently done in loadTexture() below + if (mUseLinearFiltering) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (needsDithering()) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } +} + +bool LayerBase::isSupportedYuvFormat(int format) const +{ + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_P: + case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_I: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return true; + } + return false; +} + +void LayerBase::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) const +{ + if (texture->name == -1U) { + // uh? + return; + } + + glBindTexture(GL_TEXTURE_2D, texture->name); + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is a limited form of stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); + unpack = 1 << ((unpack > 3) ? 3 : unpack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + + /* + * round to POT if needed + */ + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + texture->NPOTAdjust = true; + } + + if (texture->NPOTAdjust) { + // find the smallest power-of-two that will accommodate our surface + texture->potWidth = 1 << (31 - clz(t.width)); + texture->potHeight = 1 << (31 - clz(t.height)); + if (texture->potWidth < t.width) texture->potWidth <<= 1; + if (texture->potHeight < t.height) texture->potHeight <<= 1; + texture->wScale = float(t.width) / texture->potWidth; + texture->hScale = float(t.height) / texture->potHeight; + } else { + texture->potWidth = t.width; + texture->potHeight = t.height; + } + + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture->width != t.width || texture->height != t.height) { + texture->width = t.width; + texture->height = t.height; + + // texture size changed, we need to create a new one + bounds.set(Rect(t.width, t.height)); + if (t.width == texture->potWidth && + t.height == texture->potHeight) { + // we can do it one pass + data = t.data; + } + + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture->potWidth, texture->potHeight, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, texture->name, t.format); + } + } + if (!data) { + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride*4); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_LUMINANCE, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride); + } + } +} + +status_t LayerBase::initializeEglImage( + const sp<GraphicBuffer>& buffer, Texture* texture) +{ + status_t err = NO_ERROR; + + // we need to recreate the texture + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + + // free the previous image + if (texture->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, texture->image); + texture->image = EGL_NO_IMAGE_KHR; + } + + // construct an EGL_NATIVE_BUFFER_ANDROID + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + // create the new EGLImageKHR + const EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, EGL_NONE + }; + texture->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (texture->image != EGL_NO_IMAGE_KHR) { + glBindTexture(GL_TEXTURE_2D, texture->name); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, + (GLeglImageOES)texture->image); + GLint error = glGetError(); + if (UNLIKELY(error != GL_NO_ERROR)) { + LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " + "failed err=0x%04x", + this, texture->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + texture->NPOTAdjust = false; + texture->dirty = false; + texture->width = clientBuf->width; + texture->height = clientBuf->height; + } + } else { + LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x", + this, eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + + +// --------------------------------------------------------------------------- + +int32_t LayerBaseClient::sIdentity = 0; + +LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))) +{ + lcblk = new SharedBufferServer( + client->ctrlblk, i, NUM_BUFFERS, + mIdentity); +} + +void LayerBaseClient::onFirstRef() +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + client->bindLayer(this, mIndex); + } +} + +LayerBaseClient::~LayerBaseClient() +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + client->free(mIndex); + } + delete lcblk; +} + +int32_t LayerBaseClient::serverIndex() const +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + return (client->cid<<16)|mIndex; + } + return 0xFFFF0000 | mIndex; +} + +sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() +{ + sp<Surface> s; + Mutex::Autolock _l(mLock); + s = mClientSurface.promote(); + if (s == 0) { + s = createSurface(); + mClientSurface = s; + } + return s; +} + +sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const +{ + return new Surface(mFlinger, clientIndex(), mIdentity, + const_cast<LayerBaseClient *>(this)); +} + +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void LayerBaseClient::onRemoved() +{ + // wake up the condition + lcblk->setStatus(NO_INIT); +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::Surface::Surface( + const sp<SurfaceFlinger>& flinger, + SurfaceID id, int identity, + const sp<LayerBaseClient>& owner) + : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner) +{ +} + +LayerBaseClient::Surface::~Surface() +{ + /* + * This is a good place to clean-up all client resources + */ + + // destroy client resources + sp<LayerBaseClient> layer = getOwner(); + if (layer != 0) { + mFlinger->destroySurface(layer); + } +} + +sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const { + sp<LayerBaseClient> owner(mOwner.promote()); + return owner; +} + +status_t LayerBaseClient::Surface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + return BnSurface::onTransact(code, data, reply, flags); +} + +sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage) +{ + return NULL; +} + +status_t LayerBaseClient::Surface::registerBuffers( + const ISurface::BufferHeap& buffers) +{ + return INVALID_OPERATION; +} + +void LayerBaseClient::Surface::postBuffer(ssize_t offset) +{ +} + +void LayerBaseClient::Surface::unregisterBuffers() +{ +} + +sp<OverlayRef> LayerBaseClient::Surface::createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation) +{ + return NULL; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h new file mode 100644 index 0000000..62ec839 --- /dev/null +++ b/services/surfaceflinger/LayerBase.h @@ -0,0 +1,389 @@ +/* + * Copyright (C) 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_LAYER_BASE_H +#define ANDROID_LAYER_BASE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <utils/RefBase.h> + +#include <ui/Region.h> +#include <ui/Overlay.h> + +#include <surfaceflinger/ISurfaceFlingerClient.h> +#include <private/surfaceflinger/SharedBufferStack.h> +#include <private/surfaceflinger/LayerState.h> + +#include <pixelflinger/pixelflinger.h> + +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class DisplayHardware; +class Client; +class GraphicBuffer; +class GraphicPlane; +class SurfaceFlinger; + +// --------------------------------------------------------------------------- + +class LayerBase : public RefBase +{ + // poor man's dynamic_cast below + template<typename T> + struct getTypeInfoOfAnyType { + static uint32_t get() { return T::typeInfo; } + }; + + template<typename T> + struct getTypeInfoOfAnyType<T*> { + static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); } + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + template<typename T> + static T dynamicCast(LayerBase* base) { + uint32_t mostDerivedInfo = base->getTypeInfo(); + uint32_t castToInfo = getTypeInfoOfAnyType<T>::get(); + if ((mostDerivedInfo & castToInfo) == castToInfo) + return static_cast<T>(base); + return 0; + } + + + LayerBase(SurfaceFlinger* flinger, DisplayID display); + + DisplayID dpy; + mutable bool contentDirty; + Region visibleRegionScreen; + Region transparentRegionScreen; + Region coveredRegionScreen; + + struct State { + uint32_t w; + uint32_t h; + uint32_t requested_w; + uint32_t requested_h; + uint32_t z; + uint8_t alpha; + uint8_t flags; + uint8_t reserved[2]; + int32_t sequence; // changes when visible regions can change + uint32_t tint; + Transform transform; + Region transparentRegion; + }; + + void setName(const String8& name); + String8 getName() const; + + // modify current state + bool setPosition(int32_t x, int32_t y); + bool setLayer(uint32_t z); + bool setSize(uint32_t w, uint32_t h); + bool setAlpha(uint8_t alpha); + bool setMatrix(const layer_state_t::matrix22_t& matrix); + bool setTransparentRegionHint(const Region& opaque); + bool setFlags(uint8_t flags, uint8_t mask); + + void commitTransaction(); + bool requestTransaction(); + void forceVisibilityTransaction(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags); + + Rect visibleBounds() const; + void drawRegion(const Region& reg) const; + + void invalidate(); + + /** + * draw - performs some global clipping optimizations + * and calls onDraw(). + * Typically this method is not overridden, instead implement onDraw() + * to perform the actual drawing. + */ + virtual void draw(const Region& clip) const; + + /** + * onDraw - draws the surface. + */ + virtual void onDraw(const Region& clip) const = 0; + + /** + * initStates - called just after construction + */ + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + + /** + * doTransaction - process the transaction. This is a good place to figure + * out which attributes of the surface have changed. + */ + virtual uint32_t doTransaction(uint32_t transactionFlags); + + /** + * setVisibleRegion - called to set the new visible region. This gives + * a chance to update the new visible region or record the fact it changed. + */ + virtual void setVisibleRegion(const Region& visibleRegion); + + /** + * setCoveredRegion - called when the covered region changes. The covered + * region corresponds to any area of the surface that is covered + * (transparently or not) by another surface. + */ + virtual void setCoveredRegion(const Region& coveredRegion); + + /** + * validateVisibility - cache a bunch of things + */ + virtual void validateVisibility(const Transform& globalTransform); + + /** + * lockPageFlip - called each time the screen is redrawn and returns whether + * the visible regions need to be recomputed (this is a fairly heavy + * operation, so this should be set only if needed). Typically this is used + * to figure out if the content or size of a surface has changed. + */ + virtual void lockPageFlip(bool& recomputeVisibleRegions); + + /** + * unlockPageFlip - called each time the screen is redrawn. updates the + * final dirty region wrt the planeTransform. + * At this point, all visible regions, surface position and size, etc... are + * correct. + */ + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + + /** + * finishPageFlip - called after all surfaces have drawn. + */ + virtual void finishPageFlip(); + + /** + * needsBlending - true if this surface needs blending + */ + virtual bool needsBlending() const { return false; } + + /** + * needsDithering - true if this surface needs dithering + */ + virtual bool needsDithering() const { return false; } + + /** + * transformed -- true is this surface needs a to be transformed + */ + virtual bool transformed() const { return mTransformed; } + + /** + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or VNC servers. + */ + virtual bool isSecure() const { return false; } + + /** Called from the main thread, when the surface is removed from the + * draw list */ + virtual status_t ditch() { return NO_ERROR; } + + /** called with the state lock when the surface is removed from the + * current list */ + virtual void onRemoved() { }; + + + enum { // flags for doTransaction() + eVisibleRegion = 0x00000002, + }; + + + inline const State& drawingState() const { return mDrawingState; } + inline const State& currentState() const { return mCurrentState; } + inline State& currentState() { return mCurrentState; } + + static int compareCurrentStateZ( + sp<LayerBase> const * layerA, + sp<LayerBase> const * layerB) { + return layerA[0]->currentState().z - layerB[0]->currentState().z; + } + + int32_t getOrientation() const { return mOrientation; } + int tx() const { return mLeft; } + int ty() const { return mTop; } + +protected: + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); + + GLuint createTexture() const; + + struct Texture { + Texture() : name(-1U), width(0), height(0), + image(EGL_NO_IMAGE_KHR), transform(0), + NPOTAdjust(false), dirty(true) { } + GLuint name; + GLuint width; + GLuint height; + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + EGLImageKHR image; + uint32_t transform; + bool NPOTAdjust; + bool dirty; + }; + + void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g, + GLclampx b, GLclampx alpha) const; + void clearWithOpenGL(const Region& clip) const; + void drawWithOpenGL(const Region& clip, const Texture& texture) const; + void loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) const; + status_t initializeEglImage( + const sp<GraphicBuffer>& buffer, Texture* texture); + + bool isSupportedYuvFormat(int format) const; + + sp<SurfaceFlinger> mFlinger; + uint32_t mFlags; + + // cached during validateVisibility() + bool mTransformed; + bool mUseLinearFiltering; + int32_t mOrientation; + GLfixed mVertices[4][2]; + Rect mTransformedBounds; + int mLeft; + int mTop; + + // these are protected by an external lock + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + + // don't change, don't need a lock + bool mPremultipliedAlpha; + String8 mName; + mutable bool mDebug; + + + // atomic + volatile int32_t mInvalidate; + + +protected: + virtual ~LayerBase(); + +private: + LayerBase(const LayerBase& rhs); + void validateTexture(GLint textureName) const; +}; + + +// --------------------------------------------------------------------------- + +class LayerBaseClient : public LayerBase +{ +public: + class Surface; + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + // lcblk is (almost) only accessed from the main SF thread, in the places + // where it's not, a reference to Client must be held + SharedBufferServer* lcblk; + + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBaseClient(); + virtual void onFirstRef(); + + const wp<Client> client; + + inline uint32_t getIdentity() const { return mIdentity; } + inline int32_t clientIndex() const { return mIndex; } + int32_t serverIndex() const; + + + sp<Surface> getSurface(); + virtual sp<Surface> createSurface() const; + + virtual void onRemoved(); + + + class Surface : public BnSurface + { + public: + int32_t getToken() const { return mToken; } + int32_t getIdentity() const { return mIdentity; } + + protected: + Surface(const sp<SurfaceFlinger>& flinger, + SurfaceID id, int identity, + const sp<LayerBaseClient>& owner); + virtual ~Surface(); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + sp<LayerBaseClient> getOwner() const; + + private: + virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, + int32_t format, int32_t orientation); + + protected: + friend class LayerBaseClient; + sp<SurfaceFlinger> mFlinger; + int32_t mToken; + int32_t mIdentity; + wp<LayerBaseClient> mOwner; + }; + + friend class Surface; + +private: + int32_t mIndex; + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + // only read + const uint32_t mIdentity; + static int32_t sIdentity; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BASE_H diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp new file mode 100644 index 0000000..5fd7904 --- /dev/null +++ b/services/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "clz.h" +#include "BlurFilter.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; +const char* const LayerBlur::typeID = "LayerBlur"; + +// --------------------------------------------------------------------------- + +LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), + mRefreshCache(true), mCacheAge(0), mTextureName(-1U), + mWidthScale(1.0f), mHeightScale(1.0f), + mBlurFormat(GGL_PIXEL_FORMAT_RGB_565) +{ +} + +LayerBlur::~LayerBlur() +{ + if (mTextureName != -1U) { + glDeleteTextures(1, &mTextureName); + } +} + +void LayerBlur::setVisibleRegion(const Region& visibleRegion) +{ + LayerBaseClient::setVisibleRegion(visibleRegion); + if (visibleRegionScreen.isEmpty()) { + if (mTextureName != -1U) { + // We're not visible, free the texture up. + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &mTextureName); + mTextureName = -1U; + } + } +} + +uint32_t LayerBlur::doTransaction(uint32_t flags) +{ + // we're doing a transaction, refresh the cache! + if (!mFlinger->isFrozen()) { + mRefreshCache = true; + mCacheDirty = true; + flags |= eVisibleRegion; + this->contentDirty = true; + } + return LayerBase::doTransaction(flags); +} + +void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(!visibleRegionScreen.isEmpty())) { + // if anything visible below us is invalidated, the cache becomes dirty + if (!mCacheDirty && + !visibleRegionScreen.intersect(outDirtyRegion).isEmpty()) { + mCacheDirty = true; + } + if (mCacheDirty) { + if (!mFlinger->isFrozen()) { + // update everything below us that is visible + outDirtyRegion.orSelf(visibleRegionScreen); + nsecs_t now = systemTime(); + if ((now - mCacheAge) >= ms2ns(500)) { + mCacheAge = now; + mRefreshCache = true; + mCacheDirty = false; + } else { + if (!mAutoRefreshPending) { + mFlinger->signalDelayedEvent(ms2ns(500)); + mAutoRefreshPending = true; + } + } + } + } + } + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBlur::onDraw(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + int x = mTransformedBounds.left; + int y = mTransformedBounds.top; + int w = mTransformedBounds.width(); + int h = mTransformedBounds.height(); + GLint X = x; + GLint Y = fbHeight - (y + h); + if (X < 0) { + w += X; + X = 0; + } + if (Y < 0) { + h += Y; + Y = 0; + } + if (w<0 || h<0) { + // we're outside of the framebuffer + return; + } + + if (mTextureName == -1U) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + glGenTextures(1, &mTextureName); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &mReadFormat); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &mReadType); + if (mReadFormat != GL_RGB || mReadType != GL_UNSIGNED_SHORT_5_6_5) { + mReadFormat = GL_RGBA; + mReadType = GL_UNSIGNED_BYTE; + mBlurFormat = GGL_PIXEL_FORMAT_RGBX_8888; + } + } + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mTextureName); + + if (mRefreshCache) { + mRefreshCache = false; + mAutoRefreshPending = false; + + int32_t pixelSize = 4; + int32_t s = w; + if (mReadType == GL_UNSIGNED_SHORT_5_6_5) { + // allocate enough memory for 4-bytes (2 pixels) aligned data + s = (w + 1) & ~1; + pixelSize = 2; + } + + uint16_t* const pixels = (uint16_t*)malloc(s*h*pixelSize); + + // This reads the frame-buffer, so a h/w GL would have to + // finish() its rendering first. we don't want to do that + // too often. Read data is 4-bytes aligned. + glReadPixels(X, Y, w, h, mReadFormat, mReadType, pixels); + + // blur that texture. + GGLSurface bl; + bl.version = sizeof(GGLSurface); + bl.width = w; + bl.height = h; + bl.stride = s; + bl.format = mBlurFormat; + bl.data = (GGLubyte*)pixels; + blurFilter(&bl, 8, 2); + + if (mFlags & (DisplayHardware::NPOT_EXTENSION)) { + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0, + mReadFormat, mReadType, pixels); + mWidthScale = 1.0f / w; + mHeightScale =-1.0f / h; + mYOffset = 0; + } else { + GLuint tw = 1 << (31 - clz(w)); + GLuint th = 1 << (31 - clz(h)); + if (tw < GLuint(w)) tw <<= 1; + if (th < GLuint(h)) th <<= 1; + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, tw, th, 0, + mReadFormat, mReadType, NULL); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, + mReadFormat, mReadType, pixels); + mWidthScale = 1.0f / tw; + mHeightScale =-1.0f / th; + mYOffset = th-h; + } + + free((void*)pixels); + } + + const State& s = drawingState(); + if (UNLIKELY(s.alpha < 0xFF)) { + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(0, 0, 0, alpha); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else { + glDisable(GL_BLEND); + } + + if (mFlags & DisplayHardware::SLOW_CONFIG) { + glDisable(GL_DITHER); + } else { + glEnable(GL_DITHER); + } + + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { + // This is a very rare scenario. + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(mWidthScale, mHeightScale, 1); + glTranslatef(-x, mYOffset - y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } else { + // NOTE: this is marginally faster with the software gl, because + // glReadPixels() reads the fb bottom-to-top, however we'll + // skip all the jaccobian computations. + Rect r; + GLint crop[4] = { 0, 0, w, h }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + y = fbHeight - (y + h); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, w, h); + } + } + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h new file mode 100644 index 0000000..5b63dec --- /dev/null +++ b/services/surfaceflinger/LayerBlur.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 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_LAYER_BLUR_H +#define ANDROID_LAYER_BLUR_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/Region.h> + +#include "LayerBase.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerBlur : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBlur(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + virtual uint32_t doTransaction(uint32_t flags); + virtual void setVisibleRegion(const Region& visibleRegion); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + +private: + bool mCacheDirty; + mutable bool mRefreshCache; + mutable bool mAutoRefreshPending; + nsecs_t mCacheAge; + mutable GLuint mTextureName; + mutable GLfloat mWidthScale; + mutable GLfloat mHeightScale; + mutable GLfloat mYOffset; + mutable GLint mReadFormat; + mutable GLint mReadType; + mutable uint32_t mBlurFormat; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BLUR_H diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp new file mode 100644 index 0000000..5c21593 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -0,0 +1,727 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <math.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> +#include <ui/FramebufferNativeWindow.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +#include <hardware/copybit.h> + +#include "LayerBuffer.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; +const char* const LayerBuffer::typeID = "LayerBuffer"; +gralloc_module_t const* LayerBuffer::sGrallocModule = 0; + +// --------------------------------------------------------------------------- + +LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + mNeedsBlending(false), mBlitEngine(0) +{ +} + +LayerBuffer::~LayerBuffer() +{ + if (mBlitEngine) { + copybit_close(mBlitEngine); + } +} + +void LayerBuffer::onFirstRef() +{ + LayerBaseClient::onFirstRef(); + mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(), + const_cast<LayerBuffer *>(this)); + + hw_module_t const* module = (hw_module_t const*)sGrallocModule; + if (!module) { + // NOTE: technically there is a race here, but it shouldn't + // cause any problem since hw_get_module() always returns + // the same value. + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { + sGrallocModule = (gralloc_module_t const *)module; + } + } + + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } +} + +sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const +{ + return mSurface; +} + +status_t LayerBuffer::ditch() +{ + mSurface.clear(); + return NO_ERROR; +} + +bool LayerBuffer::needsBlending() const { + return mNeedsBlending; +} + +void LayerBuffer::setNeedsBlending(bool blending) { + mNeedsBlending = blending; +} + +void LayerBuffer::postBuffer(ssize_t offset) +{ + sp<Source> source(getSource()); + if (source != 0) + source->postBuffer(offset); +} + +void LayerBuffer::unregisterBuffers() +{ + sp<Source> source(clearSource()); + if (source != 0) + source->unregisterBuffers(); +} + +uint32_t LayerBuffer::doTransaction(uint32_t flags) +{ + sp<Source> source(getSource()); + if (source != 0) + source->onTransaction(flags); + uint32_t res = LayerBase::doTransaction(flags); + // we always want filtering for these surfaces + mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); + return res; +} + +void LayerBuffer::unlockPageFlip(const Transform& planeTransform, + Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + sp<Source> source(getSource()); + if (source != 0) + source->onVisibilityResolved(planeTransform); + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBuffer::onDraw(const Region& clip) const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) { + source->onDraw(clip); + } else { + clearWithOpenGL(clip); + } +} + +bool LayerBuffer::transformed() const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) + return source->transformed(); + return false; +} + +void LayerBuffer::serverDestroy() +{ + sp<Source> source(clearSource()); + if (source != 0) { + source->destroy(); + } +} + +/** + * This creates a "buffer" source for this surface + */ +status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +{ + Mutex::Autolock _l(mLock); + if (mSource != 0) + return INVALID_OPERATION; + + sp<BufferSource> source = new BufferSource(*this, buffers); + + status_t result = source->getStatus(); + if (result == NO_ERROR) { + mSource = source; + } + return result; +} + +/** + * This creates an "overlay" source for this surface + */ +sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f, + int32_t orientation) +{ + sp<OverlayRef> result; + Mutex::Autolock _l(mLock); + if (mSource != 0) + return result; + + sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f, orientation); + if (result != 0) { + mSource = source; + } + return result; +} + +sp<LayerBuffer::Source> LayerBuffer::getSource() const { + Mutex::Autolock _l(mLock); + return mSource; +} + +sp<LayerBuffer::Source> LayerBuffer::clearSource() { + sp<Source> source; + Mutex::Autolock _l(mLock); + source = mSource; + mSource.clear(); + return source; +} + +// ============================================================================ +// LayerBuffer::SurfaceLayerBuffer +// ============================================================================ + +LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner) +{ +} + +LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer() +{ + unregisterBuffers(); +} + +status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers( + const ISurface::BufferHeap& buffers) +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + return owner->registerBuffers(buffers); + return NO_INIT; +} + +void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset) +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + owner->postBuffer(offset); +} + +void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers() +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + owner->unregisterBuffers(); +} + +sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation) { + sp<OverlayRef> result; + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + result = owner->createOverlay(w, h, format, orientation); + return result; +} + +// ============================================================================ +// LayerBuffer::Buffer +// ============================================================================ + +LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, + ssize_t offset, size_t bufferSize) + : mBufferHeap(buffers), mSupportsCopybit(false) +{ + NativeBuffer& src(mNativeBuffer); + src.crop.l = 0; + src.crop.t = 0; + src.crop.r = buffers.w; + src.crop.b = buffers.h; + + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; + src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset); + src.img.handle = 0; + + gralloc_module_t const * module = LayerBuffer::getGrallocModule(); + if (module && module->perform) { + int err = module->perform(module, + GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER, + buffers.heap->heapID(), bufferSize, + offset, buffers.heap->base(), + &src.img.handle); + + // we can fail here is the passed buffer is purely software + mSupportsCopybit = (err == NO_ERROR); + } + } + +LayerBuffer::Buffer::~Buffer() +{ + NativeBuffer& src(mNativeBuffer); + if (src.img.handle) { + native_handle_delete(src.img.handle); + } +} + +// ============================================================================ +// LayerBuffer::Source +// LayerBuffer::BufferSource +// LayerBuffer::OverlaySource +// ============================================================================ + +LayerBuffer::Source::Source(LayerBuffer& layer) + : mLayer(layer) +{ +} +LayerBuffer::Source::~Source() { +} +void LayerBuffer::Source::onDraw(const Region& clip) const { +} +void LayerBuffer::Source::onTransaction(uint32_t flags) { +} +void LayerBuffer::Source::onVisibilityResolved( + const Transform& planeTransform) { +} +void LayerBuffer::Source::postBuffer(ssize_t offset) { +} +void LayerBuffer::Source::unregisterBuffers() { +} +bool LayerBuffer::Source::transformed() const { + return mLayer.mTransformed; +} + +// --------------------------------------------------------------------------- + +LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0), + mUseEGLImageDirectly(true) +{ + if (buffers.heap == NULL) { + // this is allowed, but in this case, it is illegal to receive + // postBuffer(). The surface just erases the framebuffer with + // fully transparent pixels. + mBufferHeap = buffers; + mLayer.setNeedsBlending(false); + return; + } + + status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT; + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err)); + mStatus = err; + return; + } + + PixelFormatInfo info; + err = getPixelFormatInfo(buffers.format, &info); + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid format %d (%s)", + buffers.format, strerror(err)); + mStatus = err; + return; + } + + if (buffers.hor_stride<0 || buffers.ver_stride<0) { + LOGE("LayerBuffer::BufferSource: invalid parameters " + "(w=%d, h=%d, xs=%d, ys=%d)", + buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride); + mStatus = BAD_VALUE; + return; + } + + mBufferHeap = buffers; + mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); + mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; + mLayer.forceVisibilityTransaction(); +} + +LayerBuffer::BufferSource::~BufferSource() +{ + class MessageDestroyTexture : public MessageBase { + SurfaceFlinger* flinger; + GLuint name; + public: + MessageDestroyTexture( + SurfaceFlinger* flinger, GLuint name) + : flinger(flinger), name(name) { } + virtual bool handler() { + glDeleteTextures(1, &name); + return true; + } + }; + + if (mTexture.name != -1U) { + // GL textures can only be destroyed from the GL thread + mLayer.mFlinger->mEventQueue.postMessage( + new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) ); + } + if (mTexture.image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); + } +} + +void LayerBuffer::BufferSource::postBuffer(ssize_t offset) +{ + ISurface::BufferHeap buffers; + { // scope for the lock + Mutex::Autolock _l(mBufferSourceLock); + buffers = mBufferHeap; + if (buffers.heap != 0) { + const size_t memorySize = buffers.heap->getSize(); + if ((size_t(offset) + mBufferSize) > memorySize) { + LOGE("LayerBuffer::BufferSource::postBuffer() " + "invalid buffer (offset=%d, size=%d, heap-size=%d", + int(offset), int(mBufferSize), int(memorySize)); + return; + } + } + } + + sp<Buffer> buffer; + if (buffers.heap != 0) { + buffer = new LayerBuffer::Buffer(buffers, offset, mBufferSize); + if (buffer->getStatus() != NO_ERROR) + buffer.clear(); + setBuffer(buffer); + mLayer.invalidate(); + } +} + +void LayerBuffer::BufferSource::unregisterBuffers() +{ + Mutex::Autolock _l(mBufferSourceLock); + mBufferHeap.heap.clear(); + mBuffer.clear(); + mLayer.invalidate(); +} + +sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const +{ + Mutex::Autolock _l(mBufferSourceLock); + return mBuffer; +} + +void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) +{ + Mutex::Autolock _l(mBufferSourceLock); + mBuffer = buffer; +} + +bool LayerBuffer::BufferSource::transformed() const +{ + return mBufferHeap.transform ? true : Source::transformed(); +} + +void LayerBuffer::BufferSource::onDraw(const Region& clip) const +{ + sp<Buffer> ourBuffer(getBuffer()); + if (UNLIKELY(ourBuffer == 0)) { + // nothing to do, we don't have a buffer + mLayer.clearWithOpenGL(clip); + return; + } + + status_t err = NO_ERROR; + NativeBuffer src(ourBuffer->getBuffer()); + const Rect transformedBounds(mLayer.getTransformedBounds()); + + if (UNLIKELY(mTexture.name == -1LU)) { + mTexture.name = mLayer.createTexture(); + } + +#if defined(EGL_ANDROID_image_native_buffer) + if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { + err = INVALID_OPERATION; + if (ourBuffer->supportsCopybit()) { + + // there are constraints on buffers used by the GPU and these may not + // be honored here. We need to change the API so the buffers + // are allocated with gralloc. For now disable this code-path +#if 0 + // First, try to use the buffer as an EGLImage directly + if (mUseEGLImageDirectly) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + + sp<GraphicBuffer> buffer = new GraphicBuffer( + src.img.w, src.img.h, src.img.format, + GraphicBuffer::USAGE_HW_TEXTURE, + src.img.w, src.img.handle, false); + + err = mLayer.initializeEglImage(buffer, &mTexture); + if (err != NO_ERROR) { + mUseEGLImageDirectly = false; + } + } +#endif + + copybit_device_t* copybit = mLayer.mBlitEngine; + if (copybit && err != NO_ERROR) { + // create our EGLImageKHR the first time + err = initTempBuffer(); + if (err == NO_ERROR) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + const NativeBuffer& dst(mTempBuffer); + region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + err = copybit->stretch(copybit, &dst.img, &src.img, + &dst.crop, &src.crop, &clip); + if (err != NO_ERROR) { + clearTempBufferImage(); + } + } + } + } + } +#endif + else { + err = INVALID_OPERATION; + } + + if (err != NO_ERROR) { + // slower fallback + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.crop.r; + t.height = src.crop.b; + t.stride = src.img.w; + t.vstride= src.img.h; + t.format = src.img.format; + t.data = (GGLubyte*)src.img.base; + const Region dirty(Rect(t.width, t.height)); + mLayer.loadTexture(&mTexture, dirty, t); + } + + mTexture.transform = mBufferHeap.transform; + mLayer.drawWithOpenGL(clip, mTexture); +} + +status_t LayerBuffer::BufferSource::initTempBuffer() const +{ + // figure out the size we need now + const ISurface::BufferHeap& buffers(mBufferHeap); + uint32_t w = mLayer.mTransformedBounds.width(); + uint32_t h = mLayer.mTransformedBounds.height(); + if (buffers.w * h != buffers.h * w) { + int t = w; w = h; h = t; + } + + // we're in the copybit case, so make sure we can handle this blit + // we don't have to keep the aspect ratio here + copybit_device_t* copybit = mLayer.mBlitEngine; + const int down = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); + const int up = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); + if (buffers.w > w*down) w = buffers.w / down; + else if (w > buffers.w*up) w = buffers.w*up; + if (buffers.h > h*down) h = buffers.h / down; + else if (h > buffers.h*up) h = buffers.h*up; + + if (mTexture.image != EGL_NO_IMAGE_KHR) { + // we have an EGLImage, make sure the needed size didn't change + if (w!=mTexture.width || h!= mTexture.height) { + // delete the EGLImage and texture + clearTempBufferImage(); + } else { + // we're good, we have an EGLImageKHR and it's (still) the + // right size + return NO_ERROR; + } + } + + // figure out if we need linear filtering + if (buffers.w * h == buffers.h * w) { + // same pixel area, don't use filtering + mLayer.mUseLinearFiltering = false; + } + + // Allocate a temporary buffer and create the corresponding EGLImageKHR + // once the EGLImage has been created we don't need the + // graphic buffer reference anymore. + sp<GraphicBuffer> buffer = new GraphicBuffer( + w, h, HAL_PIXEL_FORMAT_RGB_565, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_HW_2D); + + status_t err = buffer->initCheck(); + if (err == NO_ERROR) { + NativeBuffer& dst(mTempBuffer); + dst.img.w = buffer->getStride(); + dst.img.h = h; + dst.img.format = buffer->getPixelFormat(); + dst.img.handle = (native_handle_t *)buffer->handle; + dst.img.base = 0; + dst.crop.l = 0; + dst.crop.t = 0; + dst.crop.r = w; + dst.crop.b = h; + + err = mLayer.initializeEglImage(buffer, &mTexture); + } + + return err; +} + +void LayerBuffer::BufferSource::clearTempBufferImage() const +{ + // delete the image + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); + + // and the associated texture (recreate a name) + glDeleteTextures(1, &mTexture.name); + Texture defaultTexture; + mTexture = defaultTexture; + mTexture.name = mLayer.createTexture(); +} + +// --------------------------------------------------------------------------- + +LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format, int32_t orientation) + : Source(layer), mVisibilityChanged(false), + mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation) +{ + overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine(); + if (overlay_dev == NULL) { + // overlays not supported + return; + } + + mOverlayDevice = overlay_dev; + overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); + if (overlay == NULL) { + // couldn't create the overlay (no memory? no more overlays?) + return; + } + + // enable dithering... + overlay_dev->setParameter(overlay_dev, overlay, + OVERLAY_DITHER, OVERLAY_ENABLE); + + mOverlay = overlay; + mWidth = overlay->w; + mHeight = overlay->h; + mFormat = overlay->format; + mWidthStride = overlay->w_stride; + mHeightStride = overlay->h_stride; + mInitialized = false; + + mOverlayHandle = overlay->getHandleRef(overlay); + + sp<OverlayChannel> channel = new OverlayChannel( &layer ); + + *overlayRef = new OverlayRef(mOverlayHandle, channel, + mWidth, mHeight, mFormat, mWidthStride, mHeightStride); + mLayer.mFlinger->signalEvent(); +} + +LayerBuffer::OverlaySource::~OverlaySource() +{ + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + } +} + +void LayerBuffer::OverlaySource::onDraw(const Region& clip) const +{ + // this would be where the color-key would be set, should we need it. + GLclampx red = 0; + GLclampx green = 0; + GLclampx blue = 0; + mLayer.clearWithOpenGL(clip, red, green, blue, 0); +} + +void LayerBuffer::OverlaySource::onTransaction(uint32_t flags) +{ + const Layer::State& front(mLayer.drawingState()); + const Layer::State& temp(mLayer.currentState()); + if (temp.sequence != front.sequence) { + mVisibilityChanged = true; + } +} + +void LayerBuffer::OverlaySource::onVisibilityResolved( + const Transform& planeTransform) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(mOverlay != 0)) { + if (mVisibilityChanged || !mInitialized) { + mVisibilityChanged = false; + mInitialized = true; + const Rect bounds(mLayer.getTransformedBounds()); + int x = bounds.left; + int y = bounds.top; + int w = bounds.width(); + int h = bounds.height(); + + // we need a lock here to protect "destroy" + Mutex::Autolock _l(mOverlaySourceLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); + // we need to combine the layer orientation and the + // user-requested orientation. + Transform finalTransform = Transform(mOrientation) * + Transform(mLayer.getOrientation()); + overlay_dev->setParameter(overlay_dev, mOverlay, + OVERLAY_TRANSFORM, finalTransform.getOrientation()); + overlay_dev->commit(overlay_dev, mOverlay); + } + } + } +} + +void LayerBuffer::OverlaySource::destroy() +{ + // we need a lock here to protect "onVisibilityResolved" + Mutex::Autolock _l(mOverlaySourceLock); + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + mOverlay = 0; + } +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h new file mode 100644 index 0000000..b176623 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.h @@ -0,0 +1,225 @@ +/* + * Copyright (C) 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_LAYER_BUFFER_H +#define ANDROID_LAYER_BUFFER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "LayerBase.h" + +struct copybit_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Buffer; +class Region; +class OverlayRef; + +// --------------------------------------------------------------------------- + +class LayerBuffer : public LayerBaseClient +{ + class Source : public LightRefBase<Source> { + public: + Source(LayerBuffer& layer); + virtual ~Source(); + virtual void onDraw(const Region& clip) const; + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + virtual void destroy() { } + protected: + LayerBuffer& mLayer; + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBuffer(); + + virtual void onFirstRef(); + virtual bool needsBlending() const; + + virtual sp<LayerBaseClient::Surface> createSurface() const; + virtual status_t ditch(); + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t flags); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual bool transformed() const; + + status_t registerBuffers(const ISurface::BufferHeap& buffers); + void postBuffer(ssize_t offset); + void unregisterBuffers(); + sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format, + int32_t orientation); + + sp<Source> getSource() const; + sp<Source> clearSource(); + void setNeedsBlending(bool blending); + Rect getTransformedBounds() const { + return mTransformedBounds; + } + + void serverDestroy(); + +private: + struct NativeBuffer { + copybit_image_t img; + copybit_rect_t crop; + }; + + static gralloc_module_t const* sGrallocModule; + static gralloc_module_t const* getGrallocModule() { + return sGrallocModule; + } + + class Buffer : public LightRefBase<Buffer> { + public: + Buffer(const ISurface::BufferHeap& buffers, + ssize_t offset, size_t bufferSize); + inline bool supportsCopybit() const { + return mSupportsCopybit; + } + inline status_t getStatus() const { + return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT; + } + inline const NativeBuffer& getBuffer() const { + return mNativeBuffer; + } + protected: + friend class LightRefBase<Buffer>; + Buffer& operator = (const Buffer& rhs); + Buffer(const Buffer& rhs); + ~Buffer(); + private: + ISurface::BufferHeap mBufferHeap; + NativeBuffer mNativeBuffer; + bool mSupportsCopybit; + }; + + class BufferSource : public Source { + public: + BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers); + virtual ~BufferSource(); + + status_t getStatus() const { return mStatus; } + sp<Buffer> getBuffer() const; + void setBuffer(const sp<Buffer>& buffer); + + virtual void onDraw(const Region& clip) const; + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + virtual void destroy() { } + private: + status_t initTempBuffer() const; + void clearTempBufferImage() const; + mutable Mutex mBufferSourceLock; + sp<Buffer> mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable LayerBase::Texture mTexture; + mutable NativeBuffer mTempBuffer; + mutable bool mUseEGLImageDirectly; + }; + + class OverlaySource : public Source { + public: + OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format, int32_t orientation); + virtual ~OverlaySource(); + virtual void onDraw(const Region& clip) const; + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + virtual void destroy(); + private: + + class OverlayChannel : public BnOverlay { + wp<LayerBuffer> mLayer; + virtual void destroy() { + sp<LayerBuffer> layer(mLayer.promote()); + if (layer != 0) { + layer->serverDestroy(); + } + } + public: + OverlayChannel(const sp<LayerBuffer>& layer) + : mLayer(layer) { + } + }; + + friend class OverlayChannel; + bool mVisibilityChanged; + + overlay_t* mOverlay; + overlay_handle_t mOverlayHandle; + overlay_control_device_t* mOverlayDevice; + uint32_t mWidth; + uint32_t mHeight; + int32_t mFormat; + int32_t mWidthStride; + int32_t mHeightStride; + int32_t mOrientation; + mutable Mutex mOverlaySourceLock; + bool mInitialized; + }; + + + class SurfaceLayerBuffer : public LayerBaseClient::Surface + { + public: + SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<LayerBuffer>& owner); + virtual ~SurfaceLayerBuffer(); + + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation); + private: + sp<LayerBuffer> getOwner() const { + return static_cast<LayerBuffer*>(Surface::getOwner().get()); + } + }; + + mutable Mutex mLock; + sp<Source> mSource; + sp<Surface> mSurface; + bool mInvalidate; + bool mNeedsBlending; + copybit_device_t* mBlitEngine; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BUFFER_H diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp new file mode 100644 index 0000000..fd61e30 --- /dev/null +++ b/services/surfaceflinger/LayerDim.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include "LayerDim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; +const char* const LayerDim::typeID = "LayerDim"; + +bool LayerDim::sUseTexture; +GLuint LayerDim::sTexId; +EGLImageKHR LayerDim::sImage; +int32_t LayerDim::sWidth; +int32_t LayerDim::sHeight; + +// --------------------------------------------------------------------------- + +LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i) +{ +} + +void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) +{ + sTexId = -1; + sImage = EGL_NO_IMAGE_KHR; + sWidth = w; + sHeight = h; + sUseTexture = false; + +#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) + +#warning "using a texture to implement LayerDim" + + /* On some h/w like msm7K, it is faster to use a texture because the + * software renderer will defer to copybit, for this to work we need to + * use an EGLImage texture so copybit can actually make use of it. + * This burns a full-screen worth of graphic memory. + */ + + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + uint32_t flags = hw.getFlags(); + + if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) { + sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565, + GraphicBuffer::USAGE_SW_WRITE_OFTEN | + GraphicBuffer::USAGE_HW_TEXTURE); + + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + glGenTextures(1, &sTexId); + glBindTexture(GL_TEXTURE_2D, sTexId); + + EGLDisplay dpy = eglGetCurrentDisplay(); + sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0); + if (sImage == EGL_NO_IMAGE_KHR) { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + return; + } + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + eglDestroyImageKHR(dpy, sImage); + LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error); + return; + } + + // initialize the texture with zeros + GGLSurface t; + buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN); + memset(t.data, 0, t.stride * t.height * 2); + buffer->unlock(); + sUseTexture = true; + } +#endif +} + +LayerDim::~LayerDim() +{ +} + +void LayerDim::onDraw(const Region& clip) const +{ + const State& s(drawingState()); + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (s.alpha>0 && (it != end)) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const GGLfixed alpha = (s.alpha << 16)/255; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0, 0, alpha); + +#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) + if (sUseTexture) { + glBindTexture(GL_TEXTURE_2D, sTexId); + glEnable(GL_TEXTURE_2D); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + const GLshort texCoords[4][2] = { + { 0, 0 }, + { 0, 1 }, + { 1, 1 }, + { 1, 0 } + }; + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_SHORT, 0, texCoords); + } else +#endif + { + glDisable(GL_TEXTURE_2D); + } + + GLshort w = sWidth; + GLshort h = sHeight; + const GLshort vertices[4][2] = { + { 0, 0 }, + { 0, h }, + { w, h }, + { w, 0 } + }; + glVertexPointer(2, GL_SHORT, 0, vertices); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h new file mode 100644 index 0000000..d4672a1 --- /dev/null +++ b/services/surfaceflinger/LayerDim.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 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_LAYER_DIM_H +#define ANDROID_LAYER_DIM_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "LayerBase.h" + +// --------------------------------------------------------------------------- + +namespace android { + +class LayerDim : public LayerBaseClient +{ + static bool sUseTexture; + static GLuint sTexId; + static EGLImageKHR sImage; + static int32_t sWidth; + static int32_t sHeight; +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerDim(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerDim(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_DIM_H diff --git a/services/surfaceflinger/MODULE_LICENSE_APACHE2 b/services/surfaceflinger/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/surfaceflinger/MODULE_LICENSE_APACHE2 diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp new file mode 100644 index 0000000..b43d801 --- /dev/null +++ b/services/surfaceflinger/MessageQueue.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2009 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 <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> + +#include "MessageQueue.h" + +namespace android { + +// --------------------------------------------------------------------------- + +void MessageList::insert(const sp<MessageBase>& node) +{ + LIST::iterator cur(mList.begin()); + LIST::iterator end(mList.end()); + while (cur != end) { + if (*node < **cur) { + mList.insert(cur, node); + return; + } + ++cur; + } + mList.insert(++end, node); +} + +void MessageList::remove(MessageList::LIST::iterator pos) +{ + mList.erase(pos); +} + +// --------------------------------------------------------------------------- + +MessageQueue::MessageQueue() + : mInvalidate(false) +{ + mInvalidateMessage = new MessageBase(INVALIDATE); +} + +MessageQueue::~MessageQueue() +{ +} + +MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) +{ + MessageList::value_type result; + + bool again; + do { + const nsecs_t timeoutTime = systemTime() + timeout; + while (true) { + Mutex::Autolock _l(mLock); + nsecs_t now = systemTime(); + nsecs_t nextEventTime = -1; + + // invalidate messages are always handled first + if (mInvalidate) { + mInvalidate = false; + mInvalidateMessage->when = now; + result = mInvalidateMessage; + break; + } + + LIST::iterator cur(mMessages.begin()); + if (cur != mMessages.end()) { + result = *cur; + } + + if (result != 0) { + if (result->when <= now) { + // there is a message to deliver + mMessages.remove(cur); + break; + } + if (timeout>=0 && timeoutTime < now) { + // we timed-out, return a NULL message + result = 0; + break; + } + nextEventTime = result->when; + result = 0; + } + + if (timeout >= 0 && nextEventTime > 0) { + if (nextEventTime > timeoutTime) { + nextEventTime = timeoutTime; + } + } + + if (nextEventTime >= 0) { + //LOGD("nextEventTime = %lld ms", nextEventTime); + if (nextEventTime > 0) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + const nsecs_t reltime = nextEventTime - systemTime(); + if (reltime > 0) { + mCondition.waitRelative(mLock, reltime); + } + } + } else { + //LOGD("going to wait"); + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mCondition.wait(mLock); + } + } + // here we're not holding the lock anymore + + if (result == 0) + break; + + again = result->handler(); + if (again) { + // the message has been processed. release our reference to it + // without holding the lock. + result = 0; + } + + } while (again); + + return result; +} + +status_t MessageQueue::postMessage( + const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) +{ + return queueMessage(message, relTime, flags); +} + +status_t MessageQueue::invalidate() { + Mutex::Autolock _l(mLock); + mInvalidate = true; + mCondition.signal(); + return NO_ERROR; +} + +status_t MessageQueue::queueMessage( + const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + message->when = systemTime() + relTime; + mMessages.insert(message); + + //LOGD("MessageQueue::queueMessage time = %lld ms", message->when); + //dumpLocked(message); + + mCondition.signal(); + return NO_ERROR; +} + +void MessageQueue::dump(const MessageList::value_type& message) +{ + Mutex::Autolock _l(mLock); + dumpLocked(message); +} + +void MessageQueue::dumpLocked(const MessageList::value_type& message) +{ + LIST::const_iterator cur(mMessages.begin()); + LIST::const_iterator end(mMessages.end()); + int c = 0; + while (cur != end) { + const char tick = (*cur == message) ? '>' : ' '; + LOGD("%c %d: msg{.what=%08x, when=%lld}", + tick, c, (*cur)->what, (*cur)->when); + ++cur; + c++; + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h new file mode 100644 index 0000000..dc8138d --- /dev/null +++ b/services/surfaceflinger/MessageQueue.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 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_MESSAGE_QUEUE_H +#define ANDROID_MESSAGE_QUEUE_H + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/List.h> + + +namespace android { + +// --------------------------------------------------------------------------- + +class MessageBase; + +class MessageList +{ + List< sp<MessageBase> > mList; + typedef List< sp<MessageBase> > LIST; +public: + typedef sp<MessageBase> value_type; + inline LIST::iterator begin() { return mList.begin(); } + inline LIST::const_iterator begin() const { return mList.begin(); } + inline LIST::iterator end() { return mList.end(); } + inline LIST::const_iterator end() const { return mList.end(); } + inline bool isEmpty() const { return mList.empty(); } + void insert(const sp<MessageBase>& node); + void remove(LIST::iterator pos); +}; + +// ============================================================================ + +class MessageBase : + public LightRefBase<MessageBase> +{ +public: + nsecs_t when; + uint32_t what; + int32_t arg0; + + MessageBase() : when(0), what(0), arg0(0) { } + MessageBase(uint32_t what, int32_t arg0=0) + : when(0), what(what), arg0(arg0) { } + + // return true if message has a handler + virtual bool handler() { return false; } + +protected: + virtual ~MessageBase() { } + +private: + friend class LightRefBase<MessageBase>; +}; + +inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) { + return lhs.when < rhs.when; +} + +// --------------------------------------------------------------------------- + +class MessageQueue +{ + typedef List< sp<MessageBase> > LIST; +public: + + // this is a work-around the multichar constant warning. A macro would + // work too, but would pollute the namespace. + template <int a, int b, int c, int d> + struct WHAT { + static const uint32_t Value = + (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)| + (uint32_t(c&0xff)<<8)|uint32_t(d&0xff); + }; + + MessageQueue(); + ~MessageQueue(); + + // pre-defined messages + enum { + INVALIDATE = WHAT<'_','p','d','t'>::Value + }; + + MessageList::value_type waitMessage(nsecs_t timeout = -1); + + status_t postMessage(const MessageList::value_type& message, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t invalidate(); + + void dump(const MessageList::value_type& message); + +private: + status_t queueMessage(const MessageList::value_type& message, + nsecs_t reltime, uint32_t flags); + void dumpLocked(const MessageList::value_type& message); + + Mutex mLock; + Condition mCondition; + MessageList mMessages; + bool mInvalidate; + MessageList::value_type mInvalidateMessage; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp new file mode 100644 index 0000000..0722fda --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -0,0 +1,1940 @@ +/* + * Copyright (C) 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryHeapBase.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBufferAllocator.h> +#include <ui/PixelFormat.h> + +#include <pixelflinger/pixelflinger.h> +#include <GLES/gl.h> + +#include "clz.h" +#include "Layer.h" +#include "LayerBlur.h" +#include "LayerBuffer.h" +#include "LayerDim.h" +#include "SurfaceFlinger.h" + +#include "DisplayHardware/DisplayHardware.h" + +/* ideally AID_GRAPHICS would be in a semi-public header + * or there would be a way to map a user/group name to its id + */ +#ifndef AID_GRAPHICS +#define AID_GRAPHICS 1003 +#endif + +#define DISPLAY_COUNT 1 + +namespace android { + +// --------------------------------------------------------------------------- + +void SurfaceFlinger::instantiate() { + defaultServiceManager()->addService( + String16("SurfaceFlinger"), new SurfaceFlinger()); +} + +void SurfaceFlinger::shutdown() { + // we should unregister here, but not really because + // when (if) the service manager goes away, all the services + // it has a reference to will leave too. +} + +// --------------------------------------------------------------------------- + +SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) + : lookup(rhs.lookup), layers(rhs.layers) +{ +} + +ssize_t SurfaceFlinger::LayerVector::indexOf( + const sp<LayerBase>& key, size_t guess) const +{ + if (guess<size() && lookup.keyAt(guess) == key) + return guess; + const ssize_t i = lookup.indexOfKey(key); + if (i>=0) { + const size_t idx = lookup.valueAt(i); + LOGE_IF(layers[idx]!=key, + "LayerVector[%p]: layers[%d]=%p, key=%p", + this, int(idx), layers[idx].get(), key.get()); + return idx; + } + return i; +} + +ssize_t SurfaceFlinger::LayerVector::add( + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) +{ + size_t count = layers.size(); + ssize_t l = 0; + ssize_t h = count-1; + ssize_t mid; + sp<LayerBase> const* a = layers.array(); + while (l <= h) { + mid = l + (h - l)/2; + const int c = cmp(a+mid, &layer); + if (c == 0) { l = mid; break; } + else if (c<0) { l = mid+1; } + else { h = mid-1; } + } + size_t order = l; + while (order<count && !cmp(&layer, a+order)) { + order++; + } + count = lookup.size(); + for (size_t i=0 ; i<count ; i++) { + if (lookup.valueAt(i) >= order) { + lookup.editValueAt(i)++; + } + } + layers.insertAt(layer, order); + lookup.add(layer, order); + return order; +} + +ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer) +{ + const ssize_t keyIndex = lookup.indexOfKey(layer); + if (keyIndex >= 0) { + const size_t index = lookup.valueAt(keyIndex); + LOGE_IF(layers[index]!=layer, + "LayerVector[%p]: layers[%u]=%p, layer=%p", + this, int(index), layers[index].get(), layer.get()); + layers.removeItemsAt(index); + lookup.removeItemsAt(keyIndex); + const size_t count = lookup.size(); + for (size_t i=0 ; i<count ; i++) { + if (lookup.valueAt(i) >= size_t(index)) { + lookup.editValueAt(i)--; + } + } + return index; + } + return NAME_NOT_FOUND; +} + +ssize_t SurfaceFlinger::LayerVector::reorder( + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) +{ + // XXX: it's a little lame. but oh well... + ssize_t err = remove(layer); + if (err >=0) + err = add(layer, cmp); + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +SurfaceFlinger::SurfaceFlinger() + : BnSurfaceComposer(), Thread(false), + mTransactionFlags(0), + mTransactionCount(0), + mResizeTransationPending(false), + mLayersRemoved(false), + mBootTime(systemTime()), + mHardwareTest("android.permission.HARDWARE_TEST"), + mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"), + mDump("android.permission.DUMP"), + mVisibleRegionsDirty(false), + mDeferReleaseConsole(false), + mFreezeDisplay(false), + mFreezeCount(0), + mFreezeDisplayTime(0), + mDebugRegion(0), + mDebugBackground(0), + mDebugInSwapBuffers(0), + mLastSwapBufferTime(0), + mDebugInTransaction(0), + mLastTransactionTime(0), + mBootFinished(false), + mConsoleSignals(0), + mSecureFrameBuffer(0) +{ + init(); +} + +void SurfaceFlinger::init() +{ + LOGI("SurfaceFlinger is starting"); + + // debugging stuff... + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.showupdates", value, "0"); + mDebugRegion = atoi(value); + property_get("debug.sf.showbackground", value, "0"); + mDebugBackground = atoi(value); + + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); +} + +SurfaceFlinger::~SurfaceFlinger() +{ + glDeleteTextures(1, &mWormholeTexName); +} + +overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const +{ + return graphicPlane(0).displayHardware().getOverlayEngine(); +} + +sp<IMemoryHeap> SurfaceFlinger::getCblk() const +{ + return mServerHeap; +} + +sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() +{ + Mutex::Autolock _l(mStateLock); + uint32_t token = mTokens.acquire(); + + sp<Client> client = new Client(token, this); + if (client->ctrlblk == 0) { + mTokens.release(token); + return 0; + } + status_t err = mClientsMap.add(token, client); + if (err < 0) { + mTokens.release(token); + return 0; + } + sp<BClient> bclient = + new BClient(this, token, client->getControlBlockMemory()); + return bclient; +} + +void SurfaceFlinger::destroyConnection(ClientID cid) +{ + Mutex::Autolock _l(mStateLock); + sp<Client> client = mClientsMap.valueFor(cid); + if (client != 0) { + // free all the layers this client owns + Vector< wp<LayerBaseClient> > layers(client->getLayers()); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + sp<LayerBaseClient> layer(layers[i].promote()); + if (layer != 0) { + purgatorizeLayer_l(layer); + } + } + + // the resources associated with this client will be freed + // during the next transaction, after these surfaces have been + // properly removed from the screen + + // remove this client from our ClientID->Client mapping. + mClientsMap.removeItem(cid); + + // and add it to the list of disconnected clients + mDisconnectedClients.add(client); + + // request a transaction + setTransactionFlags(eTransactionNeeded); + } +} + +const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const +{ + LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy); + const GraphicPlane& plane(mGraphicPlanes[dpy]); + return plane; +} + +GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) +{ + return const_cast<GraphicPlane&>( + const_cast<SurfaceFlinger const *>(this)->graphicPlane(dpy)); +} + +void SurfaceFlinger::bootFinished() +{ + const nsecs_t now = systemTime(); + const nsecs_t duration = now - mBootTime; + LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + mBootFinished = true; + property_set("ctl.stop", "bootanim"); +} + +void SurfaceFlinger::onFirstRef() +{ + run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); + + // Wait for the main thread to be done with its initialization + mReadyToRunBarrier.wait(); +} + +static inline uint16_t pack565(int r, int g, int b) { + return (r<<11)|(g<<5)|b; +} + +status_t SurfaceFlinger::readyToRun() +{ + LOGI( "SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); + + // we only support one display currently + int dpy = 0; + + { + // initialize the main display + GraphicPlane& plane(graphicPlane(dpy)); + DisplayHardware* const hw = new DisplayHardware(this, dpy); + plane.setDisplayHardware(hw); + } + + // create the shared control-block + mServerHeap = new MemoryHeapBase(4096, + MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap"); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + + new(mServerCblk) surface_flinger_cblk_t; + + // initialize primary screen + // (other display should be initialized in the same manner, but + // asynchronously, as they could come and go. None of this is supported + // yet). + const GraphicPlane& plane(graphicPlane(dpy)); + const DisplayHardware& hw = plane.displayHardware(); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + const uint32_t f = hw.getFormat(); + hw.makeCurrent(); + + // initialize the shared control block + mServerCblk->connected |= 1<<dpy; + display_cblk_t* dcblk = mServerCblk->displays + dpy; + memset(dcblk, 0, sizeof(display_cblk_t)); + dcblk->w = plane.getWidth(); + dcblk->h = plane.getHeight(); + dcblk->format = f; + dcblk->orientation = ISurfaceComposer::eOrientationDefault; + dcblk->xdpi = hw.getDpiX(); + dcblk->ydpi = hw.getDpiY(); + dcblk->fps = hw.getRefreshRate(); + dcblk->density = hw.getDensity(); + asm volatile ("":::"memory"); + + // Initialize OpenGL|ES + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glEnableClientState(GL_VERTEX_ARRAY); + glEnable(GL_SCISSOR_TEST); + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + + const uint16_t g0 = pack565(0x0F,0x1F,0x0F); + const uint16_t g1 = pack565(0x17,0x2f,0x17); + const uint16_t textureData[4] = { g0, g1, g1, g0 }; + glGenTextures(1, &mWormholeTexName); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData); + + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, w, h, 0, 0, 1); + + LayerDim::initDimmer(this, w, h); + + mReadyToRunBarrier.open(); + + /* + * We're now ready to accept clients... + */ + + // start boot animation + property_set("ctl.start", "bootanim"); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Events Handler +#endif + +void SurfaceFlinger::waitForEvent() +{ + while (true) { + nsecs_t timeout = -1; + const nsecs_t freezeDisplayTimeout = ms2ns(5000); + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + const nsecs_t now = systemTime(); + if (mFreezeDisplayTime == 0) { + mFreezeDisplayTime = now; + } + nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); + timeout = waitTime>0 ? waitTime : 0; + } + + MessageList::value_type msg = mEventQueue.waitMessage(timeout); + + // see if we timed out + if (isFrozen()) { + const nsecs_t now = systemTime(); + nsecs_t frozenTime = (now - mFreezeDisplayTime); + if (frozenTime >= freezeDisplayTimeout) { + // we timed out and are still frozen + LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", + mFreezeDisplay, mFreezeCount); + mFreezeDisplayTime = 0; + mFreezeCount = 0; + mFreezeDisplay = false; + } + } + + if (msg != 0) { + switch (msg->what) { + case MessageQueue::INVALIDATE: + // invalidate message, just return to the main loop + return; + } + } + } +} + +void SurfaceFlinger::signalEvent() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signal() const { + // this is the IPC call + const_cast<SurfaceFlinger*>(this)->signalEvent(); +} + +void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +{ + mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Main loop +#endif + +bool SurfaceFlinger::threadLoop() +{ + waitForEvent(); + + // check for transactions + if (UNLIKELY(mConsoleSignals)) { + handleConsoleEvents(); + } + + if (LIKELY(mTransactionCount == 0)) { + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = getTransactionFlags(mask); + if (LIKELY(transactionFlags)) { + handleTransaction(transactionFlags); + } + } + + // post surfaces (if needed) + handlePageFlip(); + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + if (LIKELY(hw.canDraw() && !isFrozen())) { + // repaint the framebuffer (if needed) + handleRepaint(); + + // inform the h/w that we're done compositing + hw.compositionComplete(); + + // release the clients before we flip ('cause flip might block) + unlockClients(); + + postFramebuffer(); + } else { + // pretend we did the post + unlockClients(); + usleep(16667); // 60 fps period + } + return true; +} + +void SurfaceFlinger::postFramebuffer() +{ + if (!mInvalidRegion.isEmpty()) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const nsecs_t now = systemTime(); + mDebugInSwapBuffers = now; + hw.flip(mInvalidRegion); + mLastSwapBufferTime = systemTime() - now; + mDebugInSwapBuffers = 0; + mInvalidRegion.clear(); + } +} + +void SurfaceFlinger::handleConsoleEvents() +{ + // something to do with the console + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + + int what = android_atomic_and(0, &mConsoleSignals); + if (what & eConsoleAcquired) { + hw.acquireScreen(); + } + + if (mDeferReleaseConsole && hw.canDraw()) { + // We got the release signal before the acquire signal + mDeferReleaseConsole = false; + hw.releaseScreen(); + } + + if (what & eConsoleReleased) { + if (hw.canDraw()) { + hw.releaseScreen(); + } else { + mDeferReleaseConsole = true; + } + } + + mDirtyRegion.set(hw.bounds()); +} + +void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) +{ + Vector< sp<LayerBase> > ditchedLayers; + + { // scope for the lock + Mutex::Autolock _l(mStateLock); + const nsecs_t now = systemTime(); + mDebugInTransaction = now; + handleTransactionLocked(transactionFlags, ditchedLayers); + mLastTransactionTime = systemTime() - now; + mDebugInTransaction = 0; + } + + // do this without lock held + const size_t count = ditchedLayers.size(); + for (size_t i=0 ; i<count ; i++) { + if (ditchedLayers[i] != 0) { + //LOGD("ditching layer %p", ditchedLayers[i].get()); + ditchedLayers[i]->ditch(); + } + } +} + +void SurfaceFlinger::handleTransactionLocked( + uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers) +{ + const LayerVector& currentLayers(mCurrentState.layersSortedByZ); + const size_t count = currentLayers.size(); + + /* + * Traversal of the children + * (perform the transaction for each of them if needed) + */ + + const bool layersNeedTransaction = transactionFlags & eTraversalNeeded; + if (layersNeedTransaction) { + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer = currentLayers[i]; + uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); + if (!trFlags) continue; + + const uint32_t flags = layer->doTransaction(0); + if (flags & Layer::eVisibleRegion) + mVisibleRegionsDirty = true; + } + } + + /* + * Perform our own transaction if needed + */ + + if (transactionFlags & eTransactionNeeded) { + if (mCurrentState.orientation != mDrawingState.orientation) { + // the orientation has changed, recompute all visible regions + // and invalidate everything. + + const int dpy = 0; + const int orientation = mCurrentState.orientation; + const uint32_t type = mCurrentState.orientationType; + GraphicPlane& plane(graphicPlane(dpy)); + plane.setOrientation(orientation); + + // update the shared control block + const DisplayHardware& hw(plane.displayHardware()); + volatile display_cblk_t* dcblk = mServerCblk->displays + dpy; + dcblk->orientation = orientation; + dcblk->w = plane.getWidth(); + dcblk->h = plane.getHeight(); + + mVisibleRegionsDirty = true; + mDirtyRegion.set(hw.bounds()); + } + + if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { + // freezing or unfreezing the display -> trigger animation if needed + mFreezeDisplay = mCurrentState.freezeDisplay; + if (mFreezeDisplay) + mFreezeDisplayTime = 0; + } + + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added + mVisibleRegionsDirty = true; + } + + // some layers might have been removed, so + // we need to update the regions they're exposing. + if (mLayersRemoved) { + mLayersRemoved = false; + mVisibleRegionsDirty = true; + const LayerVector& previousLayers(mDrawingState.layersSortedByZ); + const size_t count = previousLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(previousLayers[i]); + if (currentLayers.indexOf( layer ) < 0) { + // this layer is not visible anymore + ditchedLayers.add(layer); + mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen); + } + } + } + + // get rid of all resources we don't need anymore + // (layers and clients) + free_resources_l(); + } + + commitTransaction(); +} + +sp<FreezeLock> SurfaceFlinger::getFreezeLock() const +{ + return new FreezeLock(const_cast<SurfaceFlinger *>(this)); +} + +void SurfaceFlinger::computeVisibleRegions( + LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + const DisplayHardware& hw(plane.displayHardware()); + const Region screenRegion(hw.bounds()); + + Region aboveOpaqueLayers; + Region aboveCoveredLayers; + Region dirty; + + bool secureFrameBuffer = false; + + size_t i = currentLayers.size(); + while (i--) { + const sp<LayerBase>& layer = currentLayers[i]; + layer->validateVisibility(planeTransform); + + // start with the whole surface at its current location + const Layer::State& s(layer->drawingState()); + + /* + * opaqueRegion: area of a surface that is fully opaque. + */ + Region opaqueRegion; + + /* + * visibleRegion: area of a surface that is visible on screen + * and not fully transparent. This is essentially the layer's + * footprint minus the opaque regions above it. + * Areas covered by a translucent surface are considered visible. + */ + Region visibleRegion; + + /* + * coveredRegion: area of a surface that is covered by all + * visible regions above it (which includes the translucent areas). + */ + Region coveredRegion; + + + // handle hidden surfaces by setting the visible region to empty + if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) { + const bool translucent = layer->needsBlending(); + const Rect bounds(layer->visibleBounds()); + visibleRegion.set(bounds); + visibleRegion.andSelf(screenRegion); + if (!visibleRegion.isEmpty()) { + // Remove the transparent area from the visible region + if (translucent) { + visibleRegion.subtractSelf(layer->transparentRegionScreen); + } + + // compute the opaque region + const int32_t layerOrientation = layer->getOrientation(); + if (s.alpha==255 && !translucent && + ((layerOrientation & Transform::ROT_INVALID) == false)) { + // the opaque region is the layer's footprint + opaqueRegion = visibleRegion; + } + } + } + + // Clip the covered region to the visible region + coveredRegion = aboveCoveredLayers.intersect(visibleRegion); + + // Update aboveCoveredLayers for next (lower) layer + aboveCoveredLayers.orSelf(visibleRegion); + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(aboveOpaqueLayers); + + // compute this layer's dirty region + if (layer->contentDirty) { + // we need to invalidate the whole region + dirty = visibleRegion; + // as well, as the old visible region + dirty.orSelf(layer->visibleRegionScreen); + layer->contentDirty = false; + } else { + /* compute the exposed region: + * the exposed region consists of two components: + * 1) what's VISIBLE now and was COVERED before + * 2) what's EXPOSED now less what was EXPOSED before + * + * note that (1) is conservative, we start with the whole + * visible region but only keep what used to be covered by + * something -- which mean it may have been exposed. + * + * (2) handles areas that were not covered by anything but got + * exposed because of a resize. + */ + const Region newExposed = visibleRegion - coveredRegion; + const Region oldVisibleRegion = layer->visibleRegionScreen; + const Region oldCoveredRegion = layer->coveredRegionScreen; + const Region oldExposed = oldVisibleRegion - oldCoveredRegion; + dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed); + } + dirty.subtractSelf(aboveOpaqueLayers); + + // accumulate to the screen dirty region + dirtyRegion.orSelf(dirty); + + // Update aboveOpaqueLayers for next (lower) layer + aboveOpaqueLayers.orSelf(opaqueRegion); + + // Store the visible region is screen space + layer->setVisibleRegion(visibleRegion); + layer->setCoveredRegion(coveredRegion); + + // If a secure layer is partially visible, lock-down the screen! + if (layer->isSecure() && !visibleRegion.isEmpty()) { + secureFrameBuffer = true; + } + } + + // invalidate the areas where a layer was removed + dirtyRegion.orSelf(mDirtyRegionRemovedLayer); + mDirtyRegionRemovedLayer.clear(); + + mSecureFrameBuffer = secureFrameBuffer; + opaqueRegion = aboveOpaqueLayers; +} + + +void SurfaceFlinger::commitTransaction() +{ + mDrawingState = mCurrentState; + mResizeTransationPending = false; + mTransactionCV.broadcast(); +} + +void SurfaceFlinger::handlePageFlip() +{ + bool visibleRegions = mVisibleRegionsDirty; + LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ); + visibleRegions |= lockPageFlip(currentLayers); + + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + const Region screenRegion(hw.bounds()); + if (visibleRegions) { + Region opaqueRegion; + computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); + mWormholeRegion = screenRegion.subtract(opaqueRegion); + mVisibleRegionsDirty = false; + } + + unlockPageFlip(currentLayers); + mDirtyRegion.andSelf(screenRegion); +} + +bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) +{ + bool recomputeVisibleRegions = false; + size_t count = currentLayers.size(); + sp<LayerBase> const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer = layers[i]; + layer->lockPageFlip(recomputeVisibleRegions); + } + return recomputeVisibleRegions; +} + +void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + size_t count = currentLayers.size(); + sp<LayerBase> const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer = layers[i]; + layer->unlockPageFlip(planeTransform, mDirtyRegion); + } +} + + +void SurfaceFlinger::handleRepaint() +{ + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + if (mInvalidRegion.isEmpty()) { + // nothing to do + return; + } + + if (UNLIKELY(mDebugRegion)) { + debugFlashRegions(); + } + + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + uint32_t flags = hw.getFlags(); + if ((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED)) + { + // we can redraw only what's dirty, but since SWAP_RECTANGLE only + // takes a rectangle, we must make sure to update that whole + // rectangle in that case + if (flags & DisplayHardware::SWAP_RECTANGLE) { + // FIXME: we really should be able to pass a region to + // SWAP_RECTANGLE so that we don't have to redraw all this. + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // in the BUFFER_PRESERVED case, obviously, we can update only + // what's needed and nothing more. + // NOTE: this is NOT a common case, as preserving the backbuffer + // is costly and usually involves copying the whole update back. + } + } else { + if (flags & DisplayHardware::PARTIAL_UPDATES) { + // We need to redraw the rectangle that will be updated + // (pushed to the framebuffer). + // This is needed because PARTIAL_UPDATES only takes one + // rectangle instead of a region (see DisplayHardware::flip()) + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // we need to redraw everything (the whole screen) + mDirtyRegion.set(hw.bounds()); + mInvalidRegion = mDirtyRegion; + } + } + + // compose all surfaces + composeSurfaces(mDirtyRegion); + + // clear the dirty regions + mDirtyRegion.clear(); +} + +void SurfaceFlinger::composeSurfaces(const Region& dirty) +{ + if (UNLIKELY(!mWormholeRegion.isEmpty())) { + // should never happen unless the window manager has a bug + // draw something... + drawWormhole(); + } + const SurfaceFlinger& flinger(*this); + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + sp<LayerBase> const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer = layers[i]; + const Region& visibleRegion(layer->visibleRegionScreen); + if (!visibleRegion.isEmpty()) { + const Region clip(dirty.intersect(visibleRegion)); + if (!clip.isEmpty()) { + layer->draw(clip); + } + } + } +} + +void SurfaceFlinger::unlockClients() +{ + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + sp<LayerBase> const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer = layers[i]; + layer->finishPageFlip(); + } +} + +void SurfaceFlinger::debugFlashRegions() +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t flags = hw.getFlags(); + + if (!((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED))) { + const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ? + mDirtyRegion.bounds() : hw.bounds()); + composeSurfaces(repaint); + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + static int toggle = 0; + toggle = 1 - toggle; + if (toggle) { + glColor4x(0x10000, 0, 0x10000, 0x10000); + } else { + glColor4x(0x10000, 0x10000, 0, 0x10000); + } + + Region::const_iterator it = mDirtyRegion.begin(); + Region::const_iterator const end = mDirtyRegion.end(); + while (it != end) { + const Rect& r = *it++; + GLfloat vertices[][2] = { + { r.left, r.top }, + { r.left, r.bottom }, + { r.right, r.bottom }, + { r.right, r.top } + }; + glVertexPointer(2, GL_FLOAT, 0, vertices); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + if (mInvalidRegion.isEmpty()) { + mDirtyRegion.dump("mDirtyRegion"); + mInvalidRegion.dump("mInvalidRegion"); + } + hw.flip(mInvalidRegion); + + if (mDebugRegion > 1) + usleep(mDebugRegion * 1000); + + glEnable(GL_SCISSOR_TEST); + //mDirtyRegion.dump("mDirtyRegion"); +} + +void SurfaceFlinger::drawWormhole() const +{ + const Region region(mWormholeRegion.intersect(mDirtyRegion)); + if (region.isEmpty()) + return; + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t width = hw.getWidth(); + const int32_t height = hw.getHeight(); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + + if (LIKELY(!mDebugBackground)) { + glClearColorx(0,0,0,0); + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glClear(GL_COLOR_BUFFER_BIT); + } + } else { + const GLshort vertices[][2] = { { 0, 0 }, { width, 0 }, + { width, height }, { 0, height } }; + const GLshort tcoords[][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + glTexCoordPointer(2, GL_SHORT, 0, tcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1); + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void SurfaceFlinger::debugShowFPS() const +{ + static int mFrameCount; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + if (diff > ms2ns(250)) { + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + } + // XXX: mFPS has the value we want + } + +status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer) +{ + Mutex::Autolock _l(mStateLock); + addLayer_l(layer); + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) +{ + Mutex::Autolock _l(mStateLock); + status_t err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) + setTransactionFlags(eTransactionNeeded); + return err; +} + +status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) +{ + if (layer == 0) + return BAD_VALUE; + ssize_t i = mCurrentState.layersSortedByZ.add( + layer, &LayerBase::compareCurrentStateZ); + sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + mLayerMap.add(lbc->serverIndex(), lbc); + } + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) +{ + ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); + if (index >= 0) { + mLayersRemoved = true; + sp<LayerBaseClient> layer = + LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get()); + if (layer != 0) { + mLayerMap.removeItem(layer->serverIndex()); + } + return NO_ERROR; + } + return status_t(index); +} + +status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) +{ + // remove the layer from the main list (through a transaction). + ssize_t err = removeLayer_l(layerBase); + + layerBase->onRemoved(); + + // it's possible that we don't find a layer, because it might + // have been destroyed already -- this is not technically an error + // from the user because there is a race between BClient::destroySurface(), + // ~BClient() and ~ISurface(). + return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err; +} + + +void SurfaceFlinger::free_resources_l() +{ + // free resources associated with disconnected clients + Vector< sp<Client> >& disconnectedClients(mDisconnectedClients); + const size_t count = disconnectedClients.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Client> client = disconnectedClients[i]; + mTokens.release(client->cid); + } + disconnectedClients.clear(); +} + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) +{ + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +{ + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + if (delay > 0) { + signalDelayedEvent(delay); + } else { + signalEvent(); + } + } + return old; +} + +void SurfaceFlinger::openGlobalTransaction() +{ + android_atomic_inc(&mTransactionCount); +} + +void SurfaceFlinger::closeGlobalTransaction() +{ + if (android_atomic_dec(&mTransactionCount) == 1) { + signalEvent(); + + // if there is a transaction with a resize, wait for it to + // take effect before returning. + Mutex::Autolock _l(mStateLock); + while (mResizeTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mResizeTransationPending = false; + break; + } + } + } +} + +status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 1; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fading) + return NO_ERROR; +} + +status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 0; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fading) + return NO_ERROR; +} + +int SurfaceFlinger::setOrientation(DisplayID dpy, + int orientation, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + if (mCurrentState.orientation != orientation) { + if (uint32_t(orientation)<=eOrientation270 || orientation==42) { + mCurrentState.orientationType = flags; + mCurrentState.orientation = orientation; + setTransactionFlags(eTransactionNeeded); + mTransactionCV.wait(mStateLock); + } else { + orientation = BAD_VALUE; + } + } + return orientation; +} + +sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, + const String8& name, ISurfaceFlingerClient::surface_data_t* params, + DisplayID d, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + sp<LayerBaseClient> layer; + sp<LayerBaseClient::Surface> surfaceHandle; + + if (int32_t(w|h) < 0) { + LOGE("createSurface() failed, w or h is negative (w=%d, h=%d)", + int(w), int(h)); + return surfaceHandle; + } + + Mutex::Autolock _l(mStateLock); + sp<Client> client = mClientsMap.valueFor(clientId); + if (UNLIKELY(client == 0)) { + LOGE("createSurface() failed, client not found (id=%d)", clientId); + return surfaceHandle; + } + + //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); + int32_t id = client->generateId(pid); + if (uint32_t(id) >= NUM_LAYERS_MAX) { + LOGE("createSurface() failed, generateId = %d", id); + return surfaceHandle; + } + + switch (flags & eFXSurfaceMask) { + case eFXSurfaceNormal: + if (UNLIKELY(flags & ePushBuffers)) { + layer = createPushBuffersSurfaceLocked(client, d, id, + w, h, flags); + } else { + layer = createNormalSurfaceLocked(client, d, id, + w, h, flags, format); + } + break; + case eFXSurfaceBlur: + layer = createBlurSurfaceLocked(client, d, id, w, h, flags); + break; + case eFXSurfaceDim: + layer = createDimSurfaceLocked(client, d, id, w, h, flags); + break; + } + + if (layer != 0) { + layer->setName(name); + setTransactionFlags(eTransactionNeeded); + surfaceHandle = layer->getSurface(); + if (surfaceHandle != 0) { + params->token = surfaceHandle->getToken(); + params->identity = surfaceHandle->getIdentity(); + params->width = w; + params->height = h; + params->format = format; + } + } + + return surfaceHandle; +} + +sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format) +{ + // initialize the surfaces + switch (format) { // TODO: take h/w into account + case PIXEL_FORMAT_TRANSPARENT: + case PIXEL_FORMAT_TRANSLUCENT: + format = PIXEL_FORMAT_RGBA_8888; + break; + case PIXEL_FORMAT_OPAQUE: + format = PIXEL_FORMAT_RGB_565; + break; + } + + sp<Layer> layer = new Layer(this, display, client, id); + status_t err = layer->setBuffers(w, h, format, flags); + if (LIKELY(err == NO_ERROR)) { + layer->initStates(w, h, flags); + addLayer_l(layer); + } else { + LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); + layer.clear(); + } + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBlur> layer = new LayerBlur(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerDim> layer = new LayerDim(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +status_t SurfaceFlinger::removeSurface(SurfaceID index) +{ + /* + * called by the window manager, when a surface should be marked for + * destruction. + * + * The surface is removed from the current and drawing lists, but placed + * in the purgatory queue, so it's not destroyed right-away (we need + * to wait for all client's references to go away first). + */ + + status_t err = NAME_NOT_FOUND; + Mutex::Autolock _l(mStateLock); + sp<LayerBaseClient> layer = getLayerUser_l(index); + if (layer != 0) { + err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) { + setTransactionFlags(eTransactionNeeded); + } + } + return err; +} + +status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) +{ + // called by ~ISurface() when all references are gone + + class MessageDestroySurface : public MessageBase { + SurfaceFlinger* flinger; + sp<LayerBaseClient> layer; + public: + MessageDestroySurface( + SurfaceFlinger* flinger, const sp<LayerBaseClient>& layer) + : flinger(flinger), layer(layer) { } + virtual bool handler() { + sp<LayerBaseClient> l(layer); + layer.clear(); // clear it outside of the lock; + Mutex::Autolock _l(flinger->mStateLock); + /* + * remove the layer from the current list -- chances are that it's + * not in the list anyway, because it should have been removed + * already upon request of the client (eg: window manager). + * However, a buggy client could have not done that. + * Since we know we don't have any more clients, we don't need + * to use the purgatory. + */ + status_t err = flinger->removeLayer_l(l); + LOGE_IF(err<0 && err != NAME_NOT_FOUND, + "error removing layer=%p (%s)", l.get(), strerror(-err)); + return true; + } + }; + + mEventQueue.postMessage( new MessageDestroySurface(this, layer) ); + return NO_ERROR; +} + +status_t SurfaceFlinger::setClientState( + ClientID cid, + int32_t count, + const layer_state_t* states) +{ + Mutex::Autolock _l(mStateLock); + uint32_t flags = 0; + cid <<= 16; + for (int i=0 ; i<count ; i++) { + const layer_state_t& s = states[i]; + sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid)); + if (layer != 0) { + const uint32_t what = s.what; + if (what & ePositionChanged) { + if (layer->setPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & eLayerChanged) { + if (layer->setLayer(s.z)) { + mCurrentState.layersSortedByZ.reorder( + layer, &Layer::compareCurrentStateZ); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; + } + } + if (what & eSizeChanged) { + if (layer->setSize(s.w, s.h)) { + flags |= eTraversalNeeded; + mResizeTransationPending = true; + } + } + if (what & eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & eVisibilityChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } + } + } + if (flags) { + setTransactionFlags(flags); + } + return NO_ERROR; +} + +sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const +{ + sp<LayerBaseClient> layer = mLayerMap.valueFor(s); + return layer; +} + +void SurfaceFlinger::screenReleased(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleReleased, &mConsoleSignals); + signalEvent(); +} + +void SurfaceFlinger::screenAcquired(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleAcquired, &mConsoleSignals); + signalEvent(); +} + +status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 1024; + char buffer[SIZE]; + String8 result; + if (!mDump.checkCalling()) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump SurfaceFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + } else { + + // figure out if we're stuck somewhere + const nsecs_t now = systemTime(); + const nsecs_t inSwapBuffers(mDebugInSwapBuffers); + const nsecs_t inTransaction(mDebugInTransaction); + nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; + + // Try to get the main lock, but don't insist if we can't + // (this would indicate SF is stuck, but we want to be able to + // print something in dumpsys). + int retry = 3; + while (mStateLock.tryLock()<0 && --retry>=0) { + usleep(1000000); + } + const bool locked(retry >= 0); + if (!locked) { + snprintf(buffer, SIZE, + "SurfaceFlinger appears to be unresponsive, " + "dumping anyways (no locks held)\n"); + result.append(buffer); + } + + size_t s = mClientsMap.size(); + char name[64]; + for (size_t i=0 ; i<s ; i++) { + sp<Client> client = mClientsMap.valueAt(i); + sprintf(name, " Client (id=0x%08x)", client->cid); + client->dump(name); + } + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + /*** LayerBase ***/ + const sp<LayerBase>& layer = currentLayers[i]; + const Layer::State& s = layer->drawingState(); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + layer->getTypeID(), layer.get(), + s.z, layer->tx(), layer->ty(), s.w, s.h, + layer->needsBlending(), layer->needsDithering(), + layer->contentDirty, + s.alpha, s.flags, + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); + result.append(buffer); + buffer[0] = 0; + /*** LayerBaseClient ***/ + sp<LayerBaseClient> lbc = + LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + sp<Client> client(lbc->client.promote()); + snprintf(buffer, SIZE, + " name=%s\n", lbc->getName().string()); + result.append(buffer); + snprintf(buffer, SIZE, + " id=0x%08x, client=0x%08x, identity=%u\n", + lbc->clientIndex(), client.get() ? client->cid : 0, + lbc->getIdentity()); + + result.append(buffer); + buffer[0] = 0; + } + /*** Layer ***/ + sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); + if (l != 0) { + SharedBufferStack::Statistics stats = l->lcblk->getStats(); + result.append( l->lcblk->dump(" ") ); + sp<const GraphicBuffer> buf0(l->getBuffer(0)); + sp<const GraphicBuffer> buf1(l->getBuffer(1)); + uint32_t w0=0, h0=0, s0=0; + uint32_t w1=0, h1=0, s1=0; + if (buf0 != 0) { + w0 = buf0->getWidth(); + h0 = buf0->getHeight(); + s0 = buf0->getStride(); + } + if (buf1 != 0) { + w1 = buf1->getWidth(); + h1 = buf1->getHeight(); + s1 = buf1->getStride(); + } + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," + " freezeLock=%p, dq-q-time=%u us\n", + l->pixelFormat(), + w0, h0, s0, w1, h1, s1, + l->getFreezeLock().get(), stats.totalTime); + result.append(buffer); + buffer[0] = 0; + } + s.transparentRegion.dump(result, "transparentRegion"); + layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); + layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); + } + mWormholeRegion.dump(result, "WormholeRegion"); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + snprintf(buffer, SIZE, + " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n", + mFreezeDisplay?"yes":"no", mFreezeCount, + mCurrentState.orientation, hw.canDraw()); + result.append(buffer); + snprintf(buffer, SIZE, + " last eglSwapBuffers() time: %f us\n" + " last transaction time : %f us\n", + mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0); + result.append(buffer); + if (inSwapBuffersDuration || !locked) { + snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", + inSwapBuffersDuration/1000.0); + result.append(buffer); + } + if (inTransactionDuration || !locked) { + snprintf(buffer, SIZE, " transaction time: %f us\n", + inTransactionDuration/1000.0); + result.append(buffer); + } + snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size()); + result.append(buffer); + const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + alloc.dump(result); + + if (locked) { + mStateLock.unlock(); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t SurfaceFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case CREATE_CONNECTION: + case OPEN_GLOBAL_TRANSACTION: + case CLOSE_GLOBAL_TRANSACTION: + case SET_ORIENTATION: + case FREEZE_DISPLAY: + case UNFREEZE_DISPLAY: + case BOOT_FINISHED: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) { + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + if (UNLIKELY(!mHardwareTest.checkCalling())) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + int n; + switch (code) { + case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE + return NO_ERROR; + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE + return NO_ERROR; + case 1002: // SHOW_UPDATES + n = data.readInt32(); + mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); + return NO_ERROR; + case 1003: // SHOW_BACKGROUND + n = data.readInt32(); + mDebugBackground = n ? 1 : 0; + return NO_ERROR; + case 1004:{ // repaint everything + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe + signalEvent(); + return NO_ERROR; + } + case 1005:{ // force transaction + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; + } + case 1007: // set mFreezeCount + mFreezeCount = data.readInt32(); + mFreezeDisplayTime = 0; + return NO_ERROR; + case 1010: // interrogate. + reply->writeInt32(0); + reply->writeInt32(0); + reply->writeInt32(mDebugRegion); + reply->writeInt32(mDebugBackground); + return NO_ERROR; + case 1013: { + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + reply->writeInt32(hw.getPageFlipCount()); + } + return NO_ERROR; + } + } + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) + : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) +{ + const int pgsize = getpagesize(); + const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1)); + + mCblkHeap = new MemoryHeapBase(cblksize, 0, + "SurfaceFlinger Client control-block"); + + ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) SharedClient; + } +} + +Client::~Client() { + if (ctrlblk) { + ctrlblk->~SharedClient(); // destroy our shared-structure. + } +} + +int32_t Client::generateId(int pid) +{ + const uint32_t i = clz( ~mBitmap ); + if (i >= NUM_LAYERS_MAX) { + return NO_MEMORY; + } + mPid = pid; + mInUse.add(uint8_t(i)); + mBitmap |= 1<<(31-i); + return i; +} + +status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id) +{ + ssize_t idx = mInUse.indexOf(id); + if (idx < 0) + return NAME_NOT_FOUND; + return mLayers.insertAt(layer, idx); +} + +void Client::free(int32_t id) +{ + ssize_t idx = mInUse.remove(uint8_t(id)); + if (idx >= 0) { + mBitmap &= ~(1<<(31-id)); + mLayers.removeItemsAt(idx); + } +} + +bool Client::isValid(int32_t i) const { + return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); +} + +sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { + sp<LayerBaseClient> lbc; + ssize_t idx = mInUse.indexOf(uint8_t(i)); + if (idx >= 0) { + lbc = mLayers[idx].promote(); + LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx)); + } + return lbc; +} + +void Client::dump(const char* what) +{ +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk) + : mId(cid), mFlinger(flinger), mCblk(cblk) +{ +} + +BClient::~BClient() { + // destroy all resources attached to this client + mFlinger->destroyConnection(mId); +} + +sp<IMemoryHeap> BClient::getControlBlock() const { + return mCblk; +} + +sp<ISurface> BClient::createSurface( + ISurfaceFlingerClient::surface_data_t* params, int pid, + const String8& name, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + return mFlinger->createSurface(mId, pid, name, params, display, w, h, + format, flags); +} + +status_t BClient::destroySurface(SurfaceID sid) +{ + sid |= (mId << 16); // add the client-part to id + return mFlinger->removeSurface(sid); +} + +status_t BClient::setState(int32_t count, const layer_state_t* states) +{ + return mFlinger->setClientState(mId, count, states); +} + +// --------------------------------------------------------------------------- + +GraphicPlane::GraphicPlane() + : mHw(0) +{ +} + +GraphicPlane::~GraphicPlane() { + delete mHw; +} + +bool GraphicPlane::initialized() const { + return mHw ? true : false; +} + +int GraphicPlane::getWidth() const { + return mWidth; +} + +int GraphicPlane::getHeight() const { + return mHeight; +} + +void GraphicPlane::setDisplayHardware(DisplayHardware *hw) +{ + mHw = hw; + + // initialize the display orientation transform. + // it's a constant that should come from the display driver. + int displayOrientation = ISurfaceComposer::eOrientationDefault; + char property[PROPERTY_VALUE_MAX]; + if (property_get("ro.sf.hwrotation", property, NULL) > 0) { + //displayOrientation + switch (atoi(property)) { + case 90: + displayOrientation = ISurfaceComposer::eOrientation90; + break; + case 270: + displayOrientation = ISurfaceComposer::eOrientation270; + break; + } + } + + const float w = hw->getWidth(); + const float h = hw->getHeight(); + GraphicPlane::orientationToTransfrom(displayOrientation, w, h, + &mDisplayTransform); + if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) { + mDisplayWidth = h; + mDisplayHeight = w; + } else { + mDisplayWidth = w; + mDisplayHeight = h; + } + + setOrientation(ISurfaceComposer::eOrientationDefault); +} + +status_t GraphicPlane::orientationToTransfrom( + int orientation, int w, int h, Transform* tr) +{ + uint32_t flags = 0; + switch (orientation) { + case ISurfaceComposer::eOrientationDefault: + flags = Transform::ROT_0; + break; + case ISurfaceComposer::eOrientation90: + flags = Transform::ROT_90; + break; + case ISurfaceComposer::eOrientation180: + flags = Transform::ROT_180; + break; + case ISurfaceComposer::eOrientation270: + flags = Transform::ROT_270; + break; + default: + return BAD_VALUE; + } + tr->set(flags, w, h); + return NO_ERROR; +} + +status_t GraphicPlane::setOrientation(int orientation) +{ + // If the rotation can be handled in hardware, this is where + // the magic should happen. + + const DisplayHardware& hw(displayHardware()); + const float w = mDisplayWidth; + const float h = mDisplayHeight; + mWidth = int(w); + mHeight = int(h); + + Transform orientationTransform; + GraphicPlane::orientationToTransfrom(orientation, w, h, + &orientationTransform); + if (orientation & ISurfaceComposer::eOrientationSwapMask) { + mWidth = int(h); + mHeight = int(w); + } + + mOrientation = orientation; + mGlobalTransform = mDisplayTransform * orientationTransform; + return NO_ERROR; +} + +const DisplayHardware& GraphicPlane::displayHardware() const { + return *mHw; +} + +const Transform& GraphicPlane::transform() const { + return mGlobalTransform; +} + +EGLDisplay GraphicPlane::getEGLDisplay() const { + return mHw->getEGLDisplay(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h new file mode 100644 index 0000000..d75dc15 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -0,0 +1,420 @@ +/* + * Copyright (C) 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_SURFACE_FLINGER_H +#define ANDROID_SURFACE_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/SortedVector.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include <binder/IMemory.h> +#include <binder/Permission.h> + +#include <ui/PixelFormat.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceFlingerClient.h> + +#include "Barrier.h" +#include "Layer.h" +#include "Tokenizer.h" + +#include "MessageQueue.h" + +struct copybit_device_t; +struct overlay_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class BClient; +class DisplayHardware; +class FreezeLock; +class Layer; +class LayerBuffer; + +typedef int32_t ClientID; + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +class Client : public RefBase +{ +public: + Client(ClientID cid, const sp<SurfaceFlinger>& flinger); + ~Client(); + + int32_t generateId(int pid); + void free(int32_t id); + status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id); + + inline bool isValid(int32_t i) const; + sp<LayerBaseClient> getLayerUser(int32_t i) const; + void dump(const char* what); + + const Vector< wp<LayerBaseClient> >& getLayers() const { + return mLayers; + } + + const sp<IMemoryHeap>& getControlBlockMemory() const { + return mCblkHeap; + } + + // pointer to this client's control block + SharedClient* ctrlblk; + ClientID cid; + + +private: + int getClientPid() const { return mPid; } + + int mPid; + uint32_t mBitmap; + SortedVector<uint8_t> mInUse; + Vector< wp<LayerBaseClient> > mLayers; + sp<IMemoryHeap> mCblkHeap; + sp<SurfaceFlinger> mFlinger; +}; + +// --------------------------------------------------------------------------- + +class GraphicPlane +{ +public: + static status_t orientationToTransfrom(int orientation, int w, int h, + Transform* tr); + + GraphicPlane(); + ~GraphicPlane(); + + bool initialized() const; + + void setDisplayHardware(DisplayHardware *); + status_t setOrientation(int orientation); + int getOrientation() const { return mOrientation; } + int getWidth() const; + int getHeight() const; + + const DisplayHardware& displayHardware() const; + const Transform& transform() const; + EGLDisplay getEGLDisplay() const; + +private: + GraphicPlane(const GraphicPlane&); + GraphicPlane operator = (const GraphicPlane&); + + DisplayHardware* mHw; + Transform mGlobalTransform; + Transform mDisplayTransform; + int mOrientation; + float mDisplayWidth; + float mDisplayHeight; + int mWidth; + int mHeight; +}; + +// --------------------------------------------------------------------------- + +enum { + eTransactionNeeded = 0x01, + eTraversalNeeded = 0x02 +}; + +class SurfaceFlinger : public BnSurfaceComposer, protected Thread +{ +public: + static void instantiate(); + static void shutdown(); + + SurfaceFlinger(); + virtual ~SurfaceFlinger(); + void init(); + + 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); + + // ISurfaceComposer interface + virtual sp<ISurfaceFlingerClient> createConnection(); + virtual sp<IMemoryHeap> getCblk() const; + virtual void bootFinished(); + virtual void openGlobalTransaction(); + virtual void closeGlobalTransaction(); + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); + virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); + virtual void signal() const; + + void screenReleased(DisplayID dpy); + void screenAcquired(DisplayID dpy); + + overlay_control_device_t* getOverlayEngine() const; + + + status_t removeLayer(const sp<LayerBase>& layer); + status_t addLayer(const sp<LayerBase>& layer); + status_t invalidateLayerVisibility(const sp<LayerBase>& layer); + +private: + friend class BClient; + friend class LayerBase; + friend class LayerBuffer; + friend class LayerBaseClient; + friend class LayerBaseClient::Surface; + friend class Layer; + friend class LayerBlur; + friend class LayerDim; + + sp<ISurface> createSurface(ClientID client, int pid, const String8& name, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags); + + sp<LayerBaseClient> createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format); + + sp<LayerBaseClient> createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerBaseClient> createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerBaseClient> createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + status_t removeSurface(SurfaceID surface_id); + status_t destroySurface(const sp<LayerBaseClient>& layer); + status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + + + class LayerVector { + public: + inline LayerVector() { } + LayerVector(const LayerVector&); + inline size_t size() const { return layers.size(); } + inline sp<LayerBase> const* array() const { return layers.array(); } + ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t remove(const sp<LayerBase>&); + ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const; + inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; } + private: + KeyedVector< sp<LayerBase> , size_t> lookup; + Vector< sp<LayerBase> > layers; + }; + + struct State { + State() { + orientation = ISurfaceComposer::eOrientationDefault; + freezeDisplay = 0; + } + LayerVector layersSortedByZ; + uint8_t orientation; + uint8_t orientationType; + uint8_t freezeDisplay; + }; + + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + +public: // hack to work around gcc 4.0.3 bug + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); +private: + + void waitForEvent(); +public: // hack to work around gcc 4.0.3 bug + void signalEvent(); +private: + void signalDelayedEvent(nsecs_t delay); + + void handleConsoleEvents(); + void handleTransaction(uint32_t transactionFlags); + void handleTransactionLocked( + uint32_t transactionFlags, + Vector< sp<LayerBase> >& ditchedLayers); + + void computeVisibleRegions( + LayerVector& currentLayers, + Region& dirtyRegion, + Region& wormholeRegion); + + void handlePageFlip(); + bool lockPageFlip(const LayerVector& currentLayers); + void unlockPageFlip(const LayerVector& currentLayers); + void handleRepaint(); + void postFramebuffer(); + void composeSurfaces(const Region& dirty); + void unlockClients(); + + + void destroyConnection(ClientID cid); + sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const; + status_t addLayer_l(const sp<LayerBase>& layer); + status_t removeLayer_l(const sp<LayerBase>& layer); + status_t purgatorizeLayer_l(const sp<LayerBase>& layer); + void free_resources_l(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + void commitTransaction(); + + + friend class FreezeLock; + sp<FreezeLock> getFreezeLock() const; + inline void incFreezeCount() { + if (mFreezeCount == 0) + mFreezeDisplayTime = 0; + mFreezeCount++; + } + inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } + inline bool hasFreezeRequest() const { return mFreezeDisplay; } + inline bool isFrozen() const { + return (mFreezeDisplay || mFreezeCount>0) && mBootFinished; + } + + + void debugFlashRegions(); + void debugShowFPS() const; + void drawWormhole() const; + + + mutable MessageQueue mEventQueue; + + + + // access must be protected by mStateLock + mutable Mutex mStateLock; + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + volatile int32_t mTransactionCount; + Condition mTransactionCV; + bool mResizeTransationPending; + + // protected by mStateLock (but we could use another lock) + Tokenizer mTokens; + DefaultKeyedVector<ClientID, sp<Client> > mClientsMap; + DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap; + GraphicPlane mGraphicPlanes[1]; + bool mLayersRemoved; + Vector< sp<Client> > mDisconnectedClients; + + // constant members (no synchronization needed for access) + sp<IMemoryHeap> mServerHeap; + surface_flinger_cblk_t* mServerCblk; + GLuint mWormholeTexName; + nsecs_t mBootTime; + Permission mHardwareTest; + Permission mAccessSurfaceFlinger; + Permission mDump; + + // Can only accessed from the main thread, these members + // don't need synchronization + Region mDirtyRegion; + Region mDirtyRegionRemovedLayer; + Region mInvalidRegion; + Region mWormholeRegion; + bool mVisibleRegionsDirty; + bool mDeferReleaseConsole; + bool mFreezeDisplay; + int32_t mFreezeCount; + nsecs_t mFreezeDisplayTime; + + // don't use a lock for these, we don't care + int mDebugRegion; + int mDebugBackground; + volatile nsecs_t mDebugInSwapBuffers; + nsecs_t mLastSwapBufferTime; + volatile nsecs_t mDebugInTransaction; + nsecs_t mLastTransactionTime; + bool mBootFinished; + + // these are thread safe + mutable Barrier mReadyToRunBarrier; + + // atomic variables + enum { + eConsoleReleased = 1, + eConsoleAcquired = 2 + }; + volatile int32_t mConsoleSignals; + + // only written in the main thread, only read in other threads + volatile int32_t mSecureFrameBuffer; +}; + +// --------------------------------------------------------------------------- + +class FreezeLock : public LightRefBase<FreezeLock> { + SurfaceFlinger* mFlinger; +public: + FreezeLock(SurfaceFlinger* flinger) + : mFlinger(flinger) { + mFlinger->incFreezeCount(); + } + ~FreezeLock() { + mFlinger->decFreezeCount(); + } +}; + +// --------------------------------------------------------------------------- + +class BClient : public BnSurfaceFlingerClient +{ +public: + BClient(SurfaceFlinger *flinger, ClientID cid, + const sp<IMemoryHeap>& cblk); + ~BClient(); + + // ISurfaceFlingerClient interface + virtual sp<IMemoryHeap> getControlBlock() const; + + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, const String8& name, + DisplayID display, uint32_t w, uint32_t h,PixelFormat format, + uint32_t flags); + + virtual status_t destroySurface(SurfaceID surfaceId); + virtual status_t setState(int32_t count, const layer_state_t* states); + +private: + ClientID mId; + SurfaceFlinger* mFlinger; + sp<IMemoryHeap> mCblk; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/Tokenizer.cpp b/services/surfaceflinger/Tokenizer.cpp new file mode 100644 index 0000000..be3a239 --- /dev/null +++ b/services/surfaceflinger/Tokenizer.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 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. + */ + +#include <stdio.h> + +#include "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + printf("Tokenizer (%p, size = %d)\n", this, int(c)); + for (size_t i=0 ; i<c ; i++) { + printf("%u: (%u, %u)\n", i, + uint32_t(ranges[i].first), uint32_t(ranges[i].length)); + } +} + +}; // namespace android + diff --git a/services/surfaceflinger/Tokenizer.h b/services/surfaceflinger/Tokenizer.h new file mode 100644 index 0000000..6b3057d --- /dev/null +++ b/services/surfaceflinger/Tokenizer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 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_TOKENIZER_H +#define ANDROID_TOKENIZER_H + +#include <utils/Vector.h> +#include <utils/Errors.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector<run_t> mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_TOKENIZER_H diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp new file mode 100644 index 0000000..175f989 --- /dev/null +++ b/services/surfaceflinger/Transform.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 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. + */ + +#include <math.h> + +#include <cutils/compiler.h> +#include <utils/String8.h> +#include <ui/Region.h> + +#include "Transform.h" + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +template <typename T> inline T min(T a, T b) { + return a<b ? a : b; +} +template <typename T> inline T min(T a, T b, T c) { + return min(a, min(b, c)); +} +template <typename T> inline T min(T a, T b, T c, T d) { + return min(a, b, min(c, d)); +} + +template <typename T> inline T max(T a, T b) { + return a>b ? a : b; +} +template <typename T> inline T max(T a, T b, T c) { + return max(a, max(b, c)); +} +template <typename T> inline T max(T a, T b, T c, T d) { + return max(a, b, max(c, d)); +} + +// --------------------------------------------------------------------------- + +Transform::Transform() { + reset(); +} + +Transform::Transform(const Transform& other) + : mMatrix(other.mMatrix), mType(other.mType) { +} + +Transform::Transform(uint32_t orientation) { + set(orientation, 0, 0); +} + +Transform::~Transform() { +} + +static const float EPSILON = 0.0f; + +bool Transform::isZero(float f) { + return fabs(f) <= EPSILON; +} + +bool Transform::absIsOne(float f) { + return isZero(fabs(f) - 1.0f); +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (CC_LIKELY(mType == IDENTITY)) + return rhs; + + Transform r(*this); + if (rhs.mType == IDENTITY) + return r; + + // TODO: we could use mType to optimize the matrix multiply + const mat33& A(mMatrix); + const mat33& B(rhs.mMatrix); + mat33& D(r.mMatrix); + for (int i=0 ; i<3 ; i++) { + const float v0 = A[0][i]; + const float v1 = A[1][i]; + const float v2 = A[2][i]; + D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; + D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; + D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; + } + r.mType |= rhs.mType; + + // TODO: we could recompute this value from r and rhs + r.mType &= 0xFF; + r.mType |= UNKNOWN_TYPE; + return r; +} + +float const* Transform::operator [] (int i) const { + return mMatrix[i].v; +} + +bool Transform::transformed() const { + return type() > TRANSLATE; +} + +int Transform::tx() const { + return floorf(mMatrix[2][0] + 0.5f); +} + +int Transform::ty() const { + return floorf(mMatrix[2][1] + 0.5f); +} + +void Transform::reset() { + mType = IDENTITY; + for(int i=0 ; i<3 ; i++) { + vec3& v(mMatrix[i]); + for (int j=0 ; j<3 ; j++) + v[j] = ((i==j) ? 1.0f : 0.0f); + } +} + +void Transform::set(float tx, float ty) +{ + mMatrix[2][0] = tx; + mMatrix[2][1] = ty; + mMatrix[2][2] = 1.0f; + + if (isZero(tx) && isZero(ty)) { + mType &= ~TRANSLATE; + } else { + mType |= TRANSLATE; + } +} + +void Transform::set(float a, float b, float c, float d) +{ + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; + M[0][1] = c; M[1][1] = d; + M[0][2] = 0; M[1][2] = 0; + mType = UNKNOWN_TYPE; +} + +status_t Transform::set(uint32_t flags, float w, float h) +{ + if (flags & ROT_INVALID) { + // that's not allowed! + reset(); + return BAD_VALUE; + } + + mType = flags << 8; + float sx = (flags & FLIP_H) ? -1 : 1; + float sy = (flags & FLIP_V) ? -1 : 1; + float a=0, b=0, c=0, d=0, x=0, y=0; + int xmask = 0; + + // computation of x,y + // x y + // 0 0 0 + // w 0 ROT90 + // w h FLIPH|FLIPV + // 0 h FLIPH|FLIPV|ROT90 + + if (flags & ROT_90) { + mType |= ROTATE; + b = -sy; + c = sx; + xmask = 1; + } else { + a = sx; + d = sy; + } + + if (flags & FLIP_H) { + mType ^= SCALE; + xmask ^= 1; + } + + if (flags & FLIP_V) { + mType ^= SCALE; + y = h; + } + + if ((flags & ROT_180) == ROT_180) { + mType |= ROTATE; + } + + if (xmask) { + x = w; + } + + if (!isZero(x) || !isZero(y)) { + mType |= TRANSLATE; + } + + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; M[2][0] = x; + M[0][1] = c; M[1][1] = d; M[2][1] = y; + M[0][2] = 0; M[1][2] = 0; M[2][2] = 1; + + return NO_ERROR; +} + +Transform::vec2 Transform::transform(const vec2& v) const { + vec2 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; + return r; +} + +Transform::vec3 Transform::transform(const vec3& v) const { + vec3 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; + r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; + return r; +} + +void Transform::transform(fixed1616* point, int x, int y) const +{ + const float toFixed = 65536.0f; + const mat33& M(mMatrix); + vec2 v(x, y); + v = transform(v); + point[0] = v[0] * toFixed; + point[1] = v[1] * toFixed; +} + +Rect Transform::makeBounds(int w, int h) const +{ + return transform( Rect(w, h) ); +} + +Rect Transform::transform(const Rect& bounds) const +{ + Rect r; + vec2 lt( bounds.left, bounds.top ); + vec2 rt( bounds.right, bounds.top ); + vec2 lb( bounds.left, bounds.bottom ); + vec2 rb( bounds.right, bounds.bottom ); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (CC_UNLIKELY(transformed())) { + if (CC_LIKELY(preserveRects())) { + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + while (it != end) { + out.orSelf(transform(*it++)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + out = reg.translate(tx(), ty()); + } + return out; +} + +uint32_t Transform::type() const +{ + if (mType & UNKNOWN_TYPE) { + // recompute what this transform is + + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + bool scale = false; + uint32_t flags = ROT_0; + if (isZero(b) && isZero(c)) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + if (!absIsOne(a) || !absIsOne(d)) { + scale = true; + } + } else if (isZero(a) && isZero(d)) { + flags |= ROT_90; + if (b>0) flags |= FLIP_H; + if (c<0) flags |= FLIP_V; + if (!absIsOne(b) || !absIsOne(c)) { + scale = true; + } + } else { + flags = ROT_INVALID; + } + + mType = flags << 8; + if (flags & ROT_INVALID) { + mType |= UNKNOWN; + } else { + if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) + mType |= ROTATE; + if (flags & FLIP_H) + mType ^= SCALE; + if (flags & FLIP_V) + mType ^= SCALE; + if (scale) + mType |= SCALE; + } + + if (!isZero(x) || !isZero(y)) + mType |= TRANSLATE; + } + return mType; +} + +uint32_t Transform::getType() const { + return type() & 0xFF; +} + +uint32_t Transform::getOrientation() const +{ + return (type() >> 8) & 0xFF; +} + +bool Transform::preserveRects() const +{ + return (type() & ROT_INVALID) ? false : true; +} + +void Transform::dump(const char* name) const +{ + type(); // updates the type + + String8 flags, type; + const mat33& m(mMatrix); + uint32_t orient = mType >> 8; + + if (orient&ROT_INVALID) { + flags.append("ROT_INVALID "); + } else { + if (orient&ROT_90) { + flags.append("ROT_90 "); + } else { + flags.append("ROT_0 "); + } + if (orient&FLIP_V) + flags.append("FLIP_V "); + if (orient&FLIP_H) + flags.append("FLIP_H "); + } + + if (!(mType&(SCALE|ROTATE|TRANSLATE))) + type.append("IDENTITY "); + if (mType&SCALE) + type.append("SCALE "); + if (mType&ROTATE) + type.append("ROTATE "); + if (mType&TRANSLATE) + type.append("TRANSLATE "); + + LOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string()); + LOGD("%.4f %.4f %.4f", m[0][0], m[1][0], m[2][0]); + LOGD("%.4f %.4f %.4f", m[0][1], m[1][1], m[2][1]); + LOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h new file mode 100644 index 0000000..2e5b893 --- /dev/null +++ b/services/surfaceflinger/Transform.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 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_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/Point.h> +#include <ui/Rect.h> + +namespace android { + +class Region; + +// --------------------------------------------------------------------------- + +class Transform +{ +public: + Transform(); + Transform(const Transform& other); + explicit Transform(uint32_t orientation); + ~Transform(); + + typedef int32_t fixed1616; + + // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = 0x00000001, + FLIP_V = 0x00000002, + ROT_90 = 0x00000004, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80 + }; + + enum type_mask { + IDENTITY = 0, + TRANSLATE = 0x1, + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 + }; + + // query the transform + bool transformed() const; + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + float const* operator [] (int i) const; // returns column i + int tx() const; + int ty() const; + + // modify the transform + void reset(); + void set(float tx, float ty); + void set(float a, float b, float c, float d); + status_t set(uint32_t flags, float w, float h); + + // transform data + Rect makeBounds(int w, int h) const; + void transform(fixed1616* point, int x, int y) const; + Region transform(const Region& reg) const; + Transform operator * (const Transform& rhs) const; + + // for debugging + void dump(const char* name) const; + +private: + struct vec3 { + float v[3]; + inline vec3() { } + inline vec3(float a, float b, float c) { + v[0] = a; v[1] = b; v[2] = c; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct vec2 { + float v[2]; + inline vec2() { } + inline vec2(float a, float b) { + v[0] = a; v[1] = b; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (int i) const { return v[i]; } + inline vec3& operator [] (int i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + Rect transform(const Rect& bounds) const; + uint32_t type() const; + static bool absIsOne(float f); + static bool isZero(float f); + + mat33 mMatrix; + mutable uint32_t mType; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/services/surfaceflinger/clz.cpp b/services/surfaceflinger/clz.cpp new file mode 100644 index 0000000..2456b86 --- /dev/null +++ b/services/surfaceflinger/clz.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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. + */ + +#include "clz.h" + +namespace android { + +int clz_impl(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + if (!x) return 32; + int e = 31; + if (x&0xFFFF0000) { e -=16; x >>=16; } + if (x&0x0000FF00) { e -= 8; x >>= 8; } + if (x&0x000000F0) { e -= 4; x >>= 4; } + if (x&0x0000000C) { e -= 2; x >>= 2; } + if (x&0x00000002) { e -= 1; } + return e; +#endif +} + +}; // namespace android diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h new file mode 100644 index 0000000..0ddf986 --- /dev/null +++ b/services/surfaceflinger/clz.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 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_SURFACE_FLINGER_CLZ_H + +#include <stdint.h> + +namespace android { + +int clz_impl(int32_t x); + +int inline clz(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + return clz_impl(x); +#endif +} + + +}; // namespace android + +#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */ diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/services/surfaceflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/services/surfaceflinger/tests/overlays/Android.mk b/services/surfaceflinger/tests/overlays/Android.mk new file mode 100644 index 0000000..592b601 --- /dev/null +++ b/services/surfaceflinger/tests/overlays/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + overlays.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-overlays + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/surfaceflinger/tests/overlays/overlays.cpp b/services/surfaceflinger/tests/overlays/overlays.cpp new file mode 100644 index 0000000..c248a61 --- /dev/null +++ b/services/surfaceflinger/tests/overlays/overlays.cpp @@ -0,0 +1,59 @@ +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> + +#include <ui/Overlay.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +using namespace android; + +namespace android { +class Test { +public: + static const sp<ISurface>& getISurface(const sp<Surface>& s) { + return s->getISurface(); + } +}; +}; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240, + PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers); + + // get to the isurface + sp<ISurface> isurface = Test::getISurface(surface); + printf("isurface = %p\n", isurface.get()); + + // now request an overlay + sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565); + sp<Overlay> overlay = new Overlay(ref); + + + /* + * here we can use the overlay API + */ + + overlay_buffer_t buffer; + overlay->dequeueBuffer(&buffer); + printf("buffer = %p\n", buffer); + + void* address = overlay->getBufferAddress(buffer); + printf("address = %p\n", address); + + overlay->queueBuffer(buffer); + + return 0; +} diff --git a/services/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk new file mode 100644 index 0000000..24c2d01 --- /dev/null +++ b/services/surfaceflinger/tests/resize/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + resize.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-resize + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp new file mode 100644 index 0000000..127cca3 --- /dev/null +++ b/services/surfaceflinger/tests/resize/resize.cpp @@ -0,0 +1,62 @@ +#include <cutils/memory.h> + +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <ui/Overlay.h> + +using namespace android; + +namespace android { +class Test { +public: + static const sp<ISurface>& getISurface(const sp<Surface>& s) { + return s->getISurface(); + } +}; +}; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240, + PIXEL_FORMAT_RGB_565); + + + client->openTransaction(); + surface->setLayer(100000); + client->closeTransaction(); + + Surface::SurfaceInfo info; + surface->lock(&info); + ssize_t bpr = info.s * bytesPerPixel(info.format); + android_memset16((uint16_t*)info.bits, 0xF800, bpr*info.h); + surface->unlockAndPost(); + + surface->lock(&info); + android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h); + surface->unlockAndPost(); + + client->openTransaction(); + surface->setSize(320, 240); + client->closeTransaction(); + + + IPCThreadState::self()->joinThreadPool(); + + return 0; +} |