diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-11-12 18:45:53 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-11-13 13:53:39 -0800 |
commit | cc8c35cee5de7fdf2d79a1a3716120b64301cdfe (patch) | |
tree | a9acd18ab5526d297928f96c094ca22eaa33e593 /libs | |
parent | cdcee265cad1fe10960bd3df32ac76c4afbd3963 (diff) | |
download | frameworks_native-cc8c35cee5de7fdf2d79a1a3716120b64301cdfe.zip frameworks_native-cc8c35cee5de7fdf2d79a1a3716120b64301cdfe.tar.gz frameworks_native-cc8c35cee5de7fdf2d79a1a3716120b64301cdfe.tar.bz2 |
eclair snapshot
Diffstat (limited to 'libs')
119 files changed, 12386 insertions, 11881 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp index 16a4f2d..351815b 100644 --- a/libs/audioflinger/A2dpAudioInterface.cpp +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -16,7 +16,7 @@ #include <math.h> -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #define LOG_TAG "A2dpAudioInterface" #include <utils/Log.h> #include <utils/String8.h> @@ -29,25 +29,41 @@ namespace android { // ---------------------------------------------------------------------------- -A2dpAudioInterface::A2dpAudioInterface() : - mOutput(0) +//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() { - delete mOutput; + closeOutputStream((AudioStreamOut *)mOutput); + delete mHardwareInterface; } status_t A2dpAudioInterface::initCheck() { - return 0; + if (mHardwareInterface == 0) return NO_INIT; + return mHardwareInterface->initCheck(); } AudioStreamOut* A2dpAudioInterface::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { - LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate); + 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 @@ -59,71 +75,142 @@ AudioStreamOut* A2dpAudioInterface::openOutputStream( // create new output stream A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); - if ((err = out->set(format, channelCount, sampleRate)) == NO_ERROR) { + 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( - int inputSource, int format, int channelCount, uint32_t sampleRate, - status_t *status, AudioSystem::audio_in_acoustics acoustics) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) { - if (status) - *status = -1; - return NULL; + 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 0; + return mHardwareInterface->setMicMute(state); } status_t A2dpAudioInterface::getMicMute(bool* state) { - return 0; + return mHardwareInterface->getMicMute(state); } -status_t A2dpAudioInterface::setParameter(const char *key, const char *value) +status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) { - LOGD("setParameter %s,%s\n", key, value); + 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 (!key || !value) - return -EINVAL; + if (param.size()) { + status_t hwStatus = mHardwareInterface->setParameters(param.toString()); + if (status == NO_ERROR) { + status = hwStatus; + } + } - if (strcmp(key, "a2dp_sink_address") == 0) { - return mOutput->setAddress(value); + 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); } - if (strcmp(key, "bluetooth_enabled") == 0) { - mOutput->setBluetoothEnabled(strcmp(value, "true") == 0); + key = "A2dpSuspended"; + if (param.get(key, value) == NO_ERROR) { + value = mSuspended ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); } - return 0; + String8 keyValuePairs = a2dpParam.toString(); + + if (param.size()) { + keyValuePairs += ";"; + keyValuePairs += mHardwareInterface->getParameters(param.toString()); + } + + LOGV("getParameters() %s", keyValuePairs.string()); + return keyValuePairs; } -status_t A2dpAudioInterface::setVoiceVolume(float v) +size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { - return 0; + return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); } -status_t A2dpAudioInterface::setMasterVolume(float v) +status_t A2dpAudioInterface::setVoiceVolume(float v) { - return 0; + return mHardwareInterface->setVoiceVolume(v); } -status_t A2dpAudioInterface::doRouting() +status_t A2dpAudioInterface::setMasterVolume(float v) { - return 0; + return mHardwareInterface->setMasterVolume(v); } status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) { - return 0; + return mHardwareInterface->dumpState(fd, args); } // ---------------------------------------------------------------------------- @@ -132,7 +219,7 @@ 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) + mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false) { // use any address by default strcpy(mA2dpAddress, "00:00:00:00:00:00"); @@ -140,27 +227,43 @@ A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : } status_t A2dpAudioInterface::A2dpAudioStreamOut::set( - int format, int channels, uint32_t rate) + uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate) { - LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate); + 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 (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) + 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) @@ -170,8 +273,10 @@ ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t size_t remaining = bytes; status_t status = -1; - if (!mBluetoothEnabled) { - LOGW("A2dpAudioStreamOut::write(), but bluetooth disabled"); + if (!mBluetoothEnabled || mClosing || mSuspended) { + LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \ + mBluetoothEnabled %d, mClosing %d, mSuspended %d", + mBluetoothEnabled, mClosing, mSuspended); goto Error; } @@ -219,6 +324,11 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() { int result = 0; + if (mClosing) { + LOGV("Ignore standby, closing"); + return result; + } + Mutex::Autolock lock(mLock); if (!mStandby) { @@ -230,6 +340,64 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() 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); @@ -257,15 +425,25 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enable 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; } @@ -277,5 +455,4 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<Strin return NO_ERROR; } - }; // namespace android diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h index 091e775..530e432 100644 --- a/libs/audioflinger/A2dpAudioInterface.h +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -32,38 +32,44 @@ class A2dpAudioInterface : public AudioHardwareBase class A2dpAudioStreamOut; public: - A2dpAudioInterface(); + 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); - // Temporary interface, do not use - // TODO: Replace with a more generic key:value get/set mechanism - virtual status_t setParameter(const char *key, const char *value); + 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( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + 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( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + 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 doRouting(); virtual status_t dump(int fd, const Vector<String16>& args); private: @@ -71,19 +77,22 @@ private: public: A2dpAudioStreamOut(); virtual ~A2dpAudioStreamOut(); - status_t set(int format, - int channelCount, - uint32_t sampleRate); + 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 int channelCount() const { return 2; } + 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 volume) { return INVALID_OPERATION; } + 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); private: friend class A2dpAudioInterface; @@ -92,6 +101,7 @@ private: status_t close_l(); status_t setAddress(const char* address); status_t setBluetoothEnabled(bool enabled); + status_t setSuspended(bool onOff); private: int mFd; @@ -102,11 +112,21 @@ private: 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 diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index 50d516b..f5c03bb 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -1,16 +1,30 @@ 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 \ - AudioDumpInterface.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 @@ -20,8 +34,44 @@ 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:= \ + AudioPolicyManagerGeneric.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_MODULE:= libaudiopolicygeneric + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_CFLAGS += -DWITH_A2DP +endif + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +include $(BUILD_SHARED_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -29,28 +79,45 @@ LOCAL_SRC_FILES:= \ AudioMixer.cpp.arm \ AudioResampler.cpp.arm \ AudioResamplerSinc.cpp.arm \ - AudioResamplerCubic.cpp.arm + AudioResamplerCubic.cpp.arm \ + AudioPolicyService.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ + libbinder \ libmedia \ - libhardware_legacy + libhardware_legacy \ + libaudiopolicygeneric ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) LOCAL_STATIC_LIBRARIES += libaudiointerface + LOCAL_CFLAGS += -DGENERIC_AUDIO +else + LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy +endif + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl else - LOCAL_SHARED_LIBRARIES += libaudio + LOCAL_SHARED_LIBRARIES += libdl endif LOCAL_MODULE:= libaudioflinger 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-libs) - LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils) + 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 include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp index b4940cb..858e5aa 100644 --- a/libs/audioflinger/AudioDumpInterface.cpp +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -16,6 +16,7 @@ */ #define LOG_TAG "AudioFlingerDump" +//#define LOG_NDEBUG 0 #include <stdint.h> #include <sys/types.h> @@ -28,68 +29,240 @@ namespace android { -bool gFirst = true; // true if first write after a standby - // ---------------------------------------------------------------------------- AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) + : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) { if(hw == 0) { LOGE("Dump construct hw = 0"); } mFinalInterface = hw; - mStreamOut = 0; + 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; - if(mStreamOut) delete mStreamOut; } AudioStreamOut* AudioDumpInterface::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + 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) { - AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status); + AudioStreamIn* inFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; + uint32_t lRate = 8000; - if(outFinal) { - mStreamOut = new AudioStreamOutDump(outFinal); - return mStreamOut; + + 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 { - LOGE("Dump outFinal=0"); - return 0; + 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( AudioStreamOut* finalStream) +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) { - mFinalStream = finalStream; - mOutFile = 0; + LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } AudioStreamOutDump::~AudioStreamOutDump() { + LOGV("AudioStreamOutDump destructor"); Close(); - delete mFinalStream; } ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) { ssize_t ret; - ret = mFinalStream->write(buffer, bytes); - if(!mOutFile && gFirst) { - gFirst = false; - // check if dump file exist - mOutFile = fopen(FLINGER_DUMP_NAME, "r"); - if(mOutFile) { - fclose(mOutFile); - mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + 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) { @@ -100,13 +273,105 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) status_t AudioStreamOutDump::standby() { + LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); + Close(); - gFirst = true; - return mFinalStream->standby(); + 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(); +} -void AudioStreamOutDump::Close(void) +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); @@ -114,4 +379,141 @@ void AudioStreamOutDump::Close(void) } } +// ---------------------------------------------------------------------------- + +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(); +} + +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/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index b72c94e..1136ce1 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -20,35 +20,95 @@ #include <stdint.h> #include <sys/types.h> +#include <utils/String8.h> +#include <utils/SortedVector.h> #include <hardware_legacy/AudioHardwareBase.h> namespace android { -#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump +#define AUDIO_DUMP_WAVE_HDR_SIZE 44 + +class AudioDumpInterface; class AudioStreamOutDump : public AudioStreamOut { public: - AudioStreamOutDump( AudioStreamOut* FinalStream); + 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 { return mFinalStream->sampleRate(); } - virtual size_t bufferSize() const { return mFinalStream->bufferSize(); } - virtual int channelCount() const { return mFinalStream->channelCount(); } - virtual int format() const { return mFinalStream->format(); } - virtual uint32_t latency() const { return mFinalStream->latency(); } - virtual status_t setVolume(float volume) - { return mFinalStream->setVolume(volume); } + + 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 dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); } + 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; } 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 + 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 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 { @@ -56,10 +116,13 @@ class AudioDumpInterface : public AudioHardwareBase public: AudioDumpInterface(AudioHardwareInterface* hw); virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + 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() @@ -75,21 +138,25 @@ public: virtual status_t getMicMute(bool* state) {return mFinalInterface->getMicMute(state);} - virtual status_t setParameter(const char* key, const char* value) - {return mFinalInterface->setParameter(key, value);} + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); - virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount, - uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) - { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); } + 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: - virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);} - - AudioHardwareInterface *mFinalInterface; - AudioStreamOutDump *mStreamOut; + AudioHardwareInterface *mFinalInterface; + SortedVector<AudioStreamOutDump *> mOutputs; + bool mFirstHwOutput; + SortedVector<AudioStreamInDump *> mInputs; + Mutex mLock; + String8 mPolicyCommands; + String8 mFileName; }; }; // namespace android diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index da7cc8a..ebd470f 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -24,10 +24,10 @@ #include <sys/time.h> #include <sys/resource.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/Log.h> -#include <utils/Parcel.h> -#include <utils/IPCThreadState.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> #include <utils/String16.h> #include <utils/threads.h> @@ -62,8 +62,6 @@ 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 unsigned long kBufferRecoveryInUsecs = 2000; -static const unsigned long kMaxBufferRecoveryInUsecs = 20000; static const float MAX_GAIN = 4096.0f; // retry counts for buffer fill timeout @@ -71,14 +69,10 @@ static const float MAX_GAIN = 4096.0f; static const int8_t kMaxTrackRetries = 50; static const int8_t kMaxTrackStartupRetries = 50; -static const int kStartSleepTime = 30000; -static const int kStopSleepTime = 30000; - static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 20000; -// Maximum number of pending buffers allocated by OutputTrack::write() -static const uint8_t kMaxOutputTrackBuffers = 5; +static const nsecs_t kWarningThrottle = seconds(5); #define AUDIOFLINGER_SECURITY_ENABLED 1 @@ -121,132 +115,41 @@ static bool settingsAllowed() { AudioFlinger::AudioFlinger() : BnAudioFlinger(), - mAudioHardware(0), mA2dpAudioInterface(0), mA2dpEnabled(false), mNotifyA2dpChange(false), - mForcedSpeakerCount(0), mA2dpDisableCount(0), mA2dpSuppressed(false), mForcedRoute(0), - mRouteRestoreTime(0), mMusicMuteSaved(false) + 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 - mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; - status_t status; - AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); - mHardwareStatus = AUDIO_HW_IDLE; - if (hwOutput) { - mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); - } else { - LOGE("Failed to initialize hardware output stream, status: %d", status); - } - -#ifdef WITH_A2DP - // Create A2DP interface - mA2dpAudioInterface = new A2dpAudioInterface(); - AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); - if (a2dpOutput) { - mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); - if (hwOutput) { - uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); - MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, - hwOutput->sampleRate(), - AudioSystem::PCM_16_BIT, - hwOutput->channelCount(), - frameCount); - mHardwareMixerThread->setOuputTrack(a2dpOutTrack); - } - } else { - LOGE("Failed to initialize A2DP output stream, status: %d", status); - } -#endif - - // FIXME - this should come from settings - setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); setMasterVolume(1.0f); setMasterMute(false); - - // Start record thread - mAudioRecordThread = new AudioRecordThread(mAudioHardware, this); - if (mAudioRecordThread != 0) { - mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); - } - } else { + } else { LOGE("Couldn't even initialize the stubbed audio hardware!"); } } AudioFlinger::~AudioFlinger() { - if (mAudioRecordThread != 0) { - mAudioRecordThread->exit(); - mAudioRecordThread.clear(); + while (!mRecordThreads.isEmpty()) { + // closeInput() will remove first entry from mRecordThreads + closeInput(mRecordThreads.keyAt(0)); } - mHardwareMixerThread.clear(); - delete mAudioHardware; - // deleting mA2dpAudioInterface also deletes mA2dpOutput; -#ifdef WITH_A2DP - mA2dpMixerThread.clear(); - delete mA2dpAudioInterface; -#endif -} - - -#ifdef WITH_A2DP -// setA2dpEnabled_l() must be called with AudioFlinger::mLock held -void AudioFlinger::setA2dpEnabled_l(bool enable) -{ - SortedVector < sp<MixerThread::Track> > tracks; - SortedVector < wp<MixerThread::Track> > activeTracks; - - LOGD_IF(enable, "set output to A2DP\n"); - LOGD_IF(!enable, "set output to hardware audio\n"); - - // Transfer tracks playing on MUSIC stream from one mixer to the other - if (enable) { - mHardwareMixerThread->getTracks_l(tracks, activeTracks); - mA2dpMixerThread->putTracks_l(tracks, activeTracks); - } else { - mA2dpMixerThread->getTracks_l(tracks, activeTracks); - mHardwareMixerThread->putTracks_l(tracks, activeTracks); - mA2dpMixerThread->mOutput->standby(); + while (!mPlaybackThreads.isEmpty()) { + // closeOutput() will remove first entry from mPlaybackThreads + closeOutput(mPlaybackThreads.keyAt(0)); } - mA2dpEnabled = enable; - mNotifyA2dpChange = true; - mWaitWorkCV.broadcast(); -} - -// checkA2dpEnabledChange_l() must be called with AudioFlinger::mLock held -void AudioFlinger::checkA2dpEnabledChange_l() -{ - if (mNotifyA2dpChange) { - // Notify AudioSystem of the A2DP activation/deactivation - size_t size = mNotificationClients.size(); - for (size_t i = 0; i < size; i++) { - sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); - if (binder != NULL) { - LOGV("Notifying output change to client %p", binder.get()); - sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); - client->a2dpEnabledChanged(mA2dpEnabled); - } - } - mNotifyA2dpChange = false; + if (mAudioHardware) { + delete mAudioHardware; } } -#endif // WITH_A2DP -bool AudioFlinger::streamForcedToSpeaker(int streamType) -{ - // NOTE that streams listed here must not be routed to A2DP by default: - // AudioSystem::routedToA2dpOutput(streamType) == false - return (streamType == AudioSystem::RING || - streamType == AudioSystem::ALARM || - streamType == AudioSystem::NOTIFICATION || - streamType == AudioSystem::ENFORCED_AUDIBLE); -} + status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) { @@ -276,10 +179,7 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) char buffer[SIZE]; String8 result; int hardwareStatus = mHardwareStatus; - - if (hardwareStatus == AUDIO_HW_IDLE && mHardwareMixerThread->mStandby) { - hardwareStatus = AUDIO_HW_STANDBY; - } + snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus); result.append(buffer); write(fd, result.string(), result.size()); @@ -337,13 +237,16 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) dumpClients(fd, args); dumpInternals(fd, args); - mHardwareMixerThread->dump(fd, args); -#ifdef WITH_A2DP - mA2dpMixerThread->dump(fd, args); -#endif - // dump record client - if (mAudioRecordThread != 0) mAudioRecordThread->dump(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); @@ -353,6 +256,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) return NO_ERROR; } + // IAudioFlinger interface @@ -365,9 +269,10 @@ sp<IAudioTrack> AudioFlinger::createTrack( int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, + int output, status_t *status) { - sp<MixerThread::Track> track; + sp<PlaybackThread::Track> track; sp<TrackHandle> trackHandle; sp<Client> client; wp<Client> wclient; @@ -381,6 +286,12 @@ sp<IAudioTrack> AudioFlinger::createTrack( { 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); @@ -390,20 +301,15 @@ sp<IAudioTrack> AudioFlinger::createTrack( client = new Client(this, pid); mClients.add(pid, client); } -#ifdef WITH_A2DP - if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { - track = mA2dpMixerThread->createTrack_l(client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer, &lStatus); - } else -#endif - { - track = mHardwareMixerThread->createTrack_l(client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer, &lStatus); - } + 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(); } @@ -416,52 +322,57 @@ Exit: uint32_t AudioFlinger::sampleRate(int output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->sampleRate(); - } -#endif - return mHardwareMixerThread->sampleRate(); + 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 { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->channelCount(); - } -#endif - return mHardwareMixerThread->channelCount(); + 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 { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->format(); - } -#endif - return mHardwareMixerThread->format(); + 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 { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->frameCount(); - } -#endif - return mHardwareMixerThread->frameCount(); + 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 { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->latency(); - } -#endif - return mHardwareMixerThread->latency(); + 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) @@ -478,94 +389,12 @@ status_t AudioFlinger::setMasterVolume(float value) value = 1.0f; } mHardwareStatus = AUDIO_HW_IDLE; - mHardwareMixerThread->setMasterVolume(value); -#ifdef WITH_A2DP - mA2dpMixerThread->setMasterVolume(value); -#endif - - return NO_ERROR; -} - -status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) -{ - status_t err = NO_ERROR; - - // check calling permissions - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) { - LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask); - return BAD_VALUE; - } - -#ifdef WITH_A2DP - LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), - IPCThreadState::self()->getCallingPid()); - if (mode == AudioSystem::MODE_NORMAL && - (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { - AutoMutex lock(&mLock); - - bool enableA2dp = false; - if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { - enableA2dp = true; - } - if (mA2dpDisableCount > 0) { - mA2dpSuppressed = enableA2dp; - } else { - setA2dpEnabled_l(enableA2dp); - } - LOGV("setOutput done\n"); - } - // setRouting() is always called at least for mode == AudioSystem::MODE_IN_CALL when - // SCO is enabled, whatever current mode is so we can safely handle A2DP disabling only - // in this case to avoid doing it several times. - if (mode == AudioSystem::MODE_IN_CALL && - (mask & AudioSystem::ROUTE_BLUETOOTH_SCO)) { - AutoMutex lock(&mLock); - handleRouteDisablesA2dp_l(routes); - } -#endif - // do nothing if only A2DP routing is affected - mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP; - if (mask) { - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_HW_GET_ROUTING; - uint32_t r; - err = mAudioHardware->getRouting(mode, &r); - if (err == NO_ERROR) { - r = (r & ~mask) | (routes & mask); - if (mode == AudioSystem::MODE_NORMAL || - (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { - mSavedRoute = r; - r |= mForcedRoute; - LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); - } - mHardwareStatus = AUDIO_HW_SET_ROUTING; - err = mAudioHardware->setRouting(mode, r); - } - mHardwareStatus = AUDIO_HW_IDLE; - } - return err; -} + mMasterVolume = value; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads.valueAt(i)->setMasterVolume(value); -uint32_t AudioFlinger::getRouting(int mode) const -{ - uint32_t routes = 0; - if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { - if (mode == AudioSystem::MODE_NORMAL || - (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { - routes = mSavedRoute; - } else { - mHardwareStatus = AUDIO_HW_GET_ROUTING; - mAudioHardware->getRouting(mode, &routes); - mHardwareStatus = AUDIO_HW_IDLE; - } - } else { - LOGW("Illegal value: getRouting(%d)", mode); - } - return routes; + return NO_ERROR; } status_t AudioFlinger::setMode(int mode) @@ -586,15 +415,6 @@ status_t AudioFlinger::setMode(int mode) return ret; } -int AudioFlinger::getMode() const -{ - int mode = AudioSystem::MODE_INVALID; - mHardwareStatus = AUDIO_HW_SET_MODE; - mAudioHardware->getMode(&mode); - mHardwareStatus = AUDIO_HW_IDLE; - return mode; -} - status_t AudioFlinger::setMicMute(bool state) { // check calling permissions @@ -624,60 +444,55 @@ status_t AudioFlinger::setMasterMute(bool muted) if (!settingsAllowed()) { return PERMISSION_DENIED; } - mHardwareMixerThread->setMasterMute(muted); -#ifdef WITH_A2DP - mA2dpMixerThread->setMasterMute(muted); -#endif + + mMasterMute = muted; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads.valueAt(i)->setMasterMute(muted); + return NO_ERROR; } float AudioFlinger::masterVolume() const { - return mHardwareMixerThread->masterVolume(); + return mMasterVolume; } bool AudioFlinger::masterMute() const { - return mHardwareMixerThread->masterMute(); + return mMasterMute; } -status_t AudioFlinger::setStreamVolume(int stream, float value) +status_t AudioFlinger::setStreamVolume(int stream, float value, int output) { // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || - uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) { + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return BAD_VALUE; } - status_t ret = NO_ERROR; - if (stream == AudioSystem::VOICE_CALL || - stream == AudioSystem::BLUETOOTH_SCO) { - float hwValue; - if (stream == AudioSystem::VOICE_CALL) { - hwValue = (float)AudioSystem::logToLinear(value)/100.0f; - // 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) - value = 0.01 + 0.99 * value; - } else { // (type == AudioSystem::BLUETOOTH_SCO) - hwValue = 1.0f; + AutoMutex lock(mLock); + PlaybackThread *thread = NULL; + if (output) { + thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return BAD_VALUE; } - - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_SET_VOICE_VOLUME; - ret = mAudioHardware->setVoiceVolume(hwValue); - mHardwareStatus = AUDIO_HW_IDLE; } - mHardwareMixerThread->setStreamVolume(stream, value); -#ifdef WITH_A2DP - mA2dpMixerThread->setStreamVolume(stream, value); -#endif + mStreamTypes[stream].volume = value; - return ret; + 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) @@ -687,82 +502,115 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted) return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) { return BAD_VALUE; } -#ifdef WITH_A2DP - mA2dpMixerThread->setStreamMute(stream, muted); -#endif - if (stream == AudioSystem::MUSIC) - { - AutoMutex lock(&mHardwareLock); - if (mForcedRoute != 0) - mMusicMuteSaved = muted; - else - mHardwareMixerThread->setStreamMute(stream, muted); - } else { - mHardwareMixerThread->setStreamMute(stream, muted); - } + 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) const +float AudioFlinger::streamVolume(int stream, int output) const { - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return 0.0f; } - - float volume = mHardwareMixerThread->streamVolume(stream); - // remove correction applied by setStreamVolume() - if (stream == AudioSystem::VOICE_CALL) { - volume = (volume - 0.01) / 0.99 ; + + 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 (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) { return true; } - - if (stream == AudioSystem::MUSIC && mForcedRoute != 0) - { - return mMusicMuteSaved; - } - return mHardwareMixerThread->streamMute(stream); + + return mStreamTypes[stream].mute; } bool AudioFlinger::isMusicActive() const { Mutex::Autolock _l(mLock); - #ifdef WITH_A2DP - if (isA2dpEnabled()) { - return mA2dpMixerThread->isMusicActive_l(); - } - #endif - return mHardwareMixerThread->isMusicActive_l(); + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isMusicActive()) { + return true; + } + } + return false; } -status_t AudioFlinger::setParameter(const char* key, const char* value) +status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) { - status_t result, result2; - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_SET_PARAMETER; - - LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); - result = mAudioHardware->setParameter(key, value); - if (mA2dpAudioInterface) { - result2 = mA2dpAudioInterface->setParameter(key, value); - if (result2) - result = result2; + 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; } - mHardwareStatus = AUDIO_HW_IDLE; - return result; + + // 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); + 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) { + return thread->setParameters(keyValuePairs); + } + 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) @@ -770,9 +618,24 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int cha return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); } +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; +} + 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); @@ -781,12 +644,21 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) LOGV("Adding notification client %p", binder.get()); binder->linkToDeath(this); mNotificationClients.add(binder); - client->a2dpEnabledChanged(isA2dpEnabled()); + } + + // 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); @@ -801,156 +673,242 @@ void AudioFlinger::binderDied(const wp<IBinder>& who) { } } -void AudioFlinger::removeClient(pid_t pid) +// audioConfigChanged_l() must be called with AudioFlinger::mLock held +void AudioFlinger::audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2) { + int ioHandle = 0; + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i) == thread) { + ioHandle = mPlaybackThreads.keyAt(i); + break; + } + } + if (ioHandle == 0) { + for (size_t i = 0; i < mRecordThreads.size(); i++) { + if (mRecordThreads.valueAt(i) == thread) { + ioHandle = mRecordThreads.keyAt(i); + break; + } + } + } + + if (ioHandle != 0) { + 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() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); - Mutex::Autolock _l(mLock); + LOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); mClients.removeItem(pid); } -bool AudioFlinger::isA2dpEnabled() const +// ---------------------------------------------------------------------------- + +AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger) + : Thread(false), + mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), + mFormat(0), mFrameSize(1), mStandby(false) { - return mA2dpEnabled; } -void AudioFlinger::handleForcedSpeakerRoute(int command) +AudioFlinger::ThreadBase::~ThreadBase() { - switch(command) { - case ACTIVE_TRACK_ADDED: - { - AutoMutex lock(mHardwareLock); - if (mForcedSpeakerCount++ == 0) { - if (mForcedRoute == 0) { - mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); - LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime); - if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); - usleep(mHardwareMixerThread->latency()*1000); - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareStatus = AUDIO_HW_IDLE; - // delay track start so that audio hardware has time to siwtch routes - usleep(kStartSleepTime); - } - } - mForcedRoute = AudioSystem::ROUTE_SPEAKER; - mRouteRestoreTime = 0; - } - LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); - } - break; - case ACTIVE_TRACK_REMOVED: - { - AutoMutex lock(mHardwareLock); - if (mForcedSpeakerCount > 0){ - if (--mForcedSpeakerCount == 0) { - mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); - } - LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); - } else { - LOGE("mForcedSpeakerCount is already zero"); - } - } - break; - case CHECK_ROUTE_RESTORE_TIME: - case FORCE_ROUTE_RESTORE: - if (mRouteRestoreTime) { - AutoMutex lock(mHardwareLock); - if (mRouteRestoreTime && - (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); - mForcedRoute = 0; - if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); - mHardwareStatus = AUDIO_HW_IDLE; - LOGV("Route forced to Speaker OFF %08x", mSavedRoute); - } - mRouteRestoreTime = 0; - } - } - break; + 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); + requestExit(); + mWaitWorkCV.signal(); } + requestExitAndWait(); } -#ifdef WITH_A2DP -// handleRouteDisablesA2dp_l() must be called with AudioFlinger::mLock held -void AudioFlinger::handleRouteDisablesA2dp_l(int routes) -{ - if (routes & AudioSystem::ROUTE_BLUETOOTH_SCO) { - if (mA2dpDisableCount++ == 0) { - if (mA2dpEnabled) { - setA2dpEnabled_l(false); - mA2dpSuppressed = true; - } - } - LOGV("mA2dpDisableCount incremented to %d", mA2dpDisableCount); - } else { - if (mA2dpDisableCount > 0) { - if (--mA2dpDisableCount == 0) { - if (mA2dpSuppressed) { - setA2dpEnabled_l(true); - mA2dpSuppressed = false; - } - } - LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount); - } else { - LOGV("mA2dpDisableCount is already zero"); - } +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; } -#endif -// ---------------------------------------------------------------------------- +void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param) +{ + Mutex::Autolock _l(mLock); + sendConfigEvent_l(event, param); +} -AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType) - : Thread(false), - mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), - mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), - mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), - mInWrite(false) +// sendConfigEvent_l() must be called with ThreadBase::mLock held +void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param) { - mSampleRate = output->sampleRate(); - mChannelCount = output->channelCount(); + 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(); +} - // FIXME - Current mixer implementation only supports stereo output - if (mChannelCount == 1) { - LOGE("Invalid audio hardware channel count"); +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(); +} - mFormat = output->format(); - mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t); - mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); +status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; - // FIXME - Current mixer implementation only supports stereo output: Always - // Allocate a stereo buffer even if HW output is mono. - mMixBuffer = new int16_t[mFrameCount * 2]; - memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); + 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::MixerThread::~MixerThread() + +// ---------------------------------------------------------------------------- + +AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : ThreadBase(audioFlinger), + 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; - delete mAudioMixer; } -status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args) +status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) { dumpInternals(fd, args); dumpTracks(fd, args); return NO_ERROR; } -status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args) +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 %d mixer thread tracks\n", mOutputType); + 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"); + 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) { @@ -959,9 +917,9 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a } } - snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + 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"); + 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) { @@ -976,15 +934,13 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a return NO_ERROR; } -status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); - result.append(buffer); - snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + 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); @@ -994,350 +950,545 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> result.append(buffer); snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); result.append(buffer); - snprintf(buffer, SIZE, "standby: %d\n", mStandby); - 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) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) +{ + 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) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) +{ + 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::isMusicActive() 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() == AudioSystem::MUSIC) + 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; + + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (track->isPaused()) { + track->mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d) on thread %p", track->name(), this); + } else { + track->mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d) on thread %p", track->name(), this); + } + // 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) { + LOGV("remove track (%d) and delete from mixer", track->name()); + 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, this, param2); +} + +void AudioFlinger::PlaybackThread::readOutputParameters() +{ + mSampleRate = mOutput->sampleRate(); + mChannelCount = AudioSystem::popCount(mOutput->channels()); + + mFormat = mOutput->format(); + mFrameSize = mOutput->frameSize(); + mFrameCount = mOutput->bufferSize() / mFrameSize; + + mMinBytesToWrite = (mOutput->latency() * mSampleRate * mFrameSize) / 1000; + // 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)); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : PlaybackThread(audioFlinger, output), + 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() { - unsigned long sleepTime = kBufferRecoveryInUsecs; + uint32_t sleepTime = 1000; + uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; size_t enabledTracks = 0; - nsecs_t standbyTime = systemTime(); - size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); - nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; - -#ifdef WITH_A2DP - bool outputTrackActive = false; -#endif + 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; + + while (!exitPending()) + { + processConfigEvents(); - do { enabledTracks = 0; - { // scope for the AudioFlinger::mLock - - Mutex::Autolock _l(mAudioFlinger->mLock); + { // scope for mLock -#ifdef WITH_A2DP - if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { - if (outputTrackActive) { - mAudioFlinger->mLock.unlock(); - mOutputTrack->stop(); - mAudioFlinger->mLock.lock(); - outputTrackActive = false; - } + 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; + maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); } - mAudioFlinger->checkA2dpEnabledChange_l(); -#endif const SortedVector< wp<Track> >& activeTracks = mActiveTracks; // put audio hardware into standby after short delay - if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { - // wait until we have something to do... - LOGV("Audio hardware entering standby, output %d\n", mOutputType); + 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; } - -#ifdef WITH_A2DP - if (outputTrackActive) { - mAudioFlinger->mLock.unlock(); - mOutputTrack->stop(); - mAudioFlinger->mLock.lock(); - outputTrackActive = false; - } -#endif - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); - } - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - mAudioFlinger->mWaitWorkCV.wait(mAudioFlinger->mLock); - LOGV("Audio hardware exiting standby, output %d\n", mOutputType); - - 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; - continue; - } - // Forced route to speaker is handled by hardware mixer thread - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); - } - - // find out which tracks need to be processed - size_t count = activeTracks.size(); - for (size_t i=0 ; i<count ; i++) { - sp<Track> t = activeTracks[i].promote(); - if (t == 0) continue; + if (!activeTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); - Track* const track = t.get(); - audio_track_cblk_t* cblk = track->cblk(); + if (exitPending()) break; - // 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()) - { - //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + // 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()); - // compute volume for this track - int16_t left, right; - if (track->isMuted() || mMasterMute || track->isPausing()) { - left = right = 0; - if (track->isPausing()) { - LOGV("paused(%d)", track->name()); - track->setPaused(); + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); } - } 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 = 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; - 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 { - param = AudioMixer::VOLUME; - } - } else { - param = AudioMixer::RAMP_VOLUME; - } - 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; - enabledTracks++; - } 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. - LOGV("remove(%d) from active list", track->name()); - tracksToRemove.add(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()); - tracksToRemove.add(track); - } - } - // LOGV("disable(%d)", track->name()); - mAudioMixer->disable(AudioMixer::MIXING); + standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = 1000; + continue; } } - // 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[i]; - removeActiveTrack_l(track); - if (track->isTerminated()) { - mTracks.remove(track); - deleteTrackName_l(track->mName); - } - } - } + enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); } - + if (LIKELY(enabledTracks)) { // mix buffers... mAudioMixer->process(curBuf); - -#ifdef WITH_A2DP - if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { - if (!outputTrackActive) { - LOGV("starting output track in mixer for output %d", mOutputType); - mOutputTrack->start(); - outputTrackActive = true; - } - mOutputTrack->write(curBuf, mFrameCount); + 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) { + sleepTime = maxBufferRecoveryInUsecs; + } else if (mBytesWritten != 0) { + memset (curBuf, 0, mixBufferSize); + sleepTime = 0; } -#endif + } - // output audio to hardware + if (mSuspended) { + sleepTime = maxBufferRecoveryInUsecs; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { mLastWriteTime = systemTime(); mInWrite = true; - mOutput->write(curBuf, mixBufferSize); + int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); + if (bytesWritten > 0) mBytesWritten += bytesWritten; mNumWrites++; mInWrite = false; mStandby = false; - nsecs_t temp = systemTime(); - standbyTime = temp + kStandbyTimeInNsecs; - nsecs_t delta = temp - mLastWriteTime; + nsecs_t now = systemTime(); + nsecs_t delta = now - mLastWriteTime; if (delta > maxPeriod) { - LOGW("write blocked for %llu msecs", ns2ms(delta)); mNumDelayedWrites++; - } - sleepTime = kBufferRecoveryInUsecs; - } else { -#ifdef WITH_A2DP - if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { - if (outputTrackActive) { - mOutputTrack->write(curBuf, 0); - if (mOutputTrack->bufferQueueEmpty()) { - mOutputTrack->stop(); - outputTrackActive = false; - } else { - standbyTime = systemTime() + kStandbyTimeInNsecs; - } + if ((now - lastWarning) > kWarningThrottle) { + LOGW("write blocked for %llu msecs, %d delayed writes, thread %p", + ns2ms(delta), mNumDelayedWrites, this); + lastWarning = now; } } -#endif - // There was nothing to mix this round, which means all - // active tracks were late. Sleep a little bit to give - // them another chance. If we're too late, the audio - // hardware will zero-fill for us. - //LOGV("no buffers - usleep(%lu)", sleepTime); + } else { usleep(sleepTime); - if (sleepTime < kMaxBufferRecoveryInUsecs) { - sleepTime += kBufferRecoveryInUsecs; - } } // 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(); - } while (true); - - return false; -} + } -status_t AudioFlinger::MixerThread::readyToRun() -{ - if (mSampleRate == 0) { - LOGE("No working audio driver found."); - return NO_INIT; + if (!mStandby) { + mOutput->standby(); } - LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); - return NO_ERROR; + + LOGV("MixerThread %p exiting", this); + return false; } -void AudioFlinger::MixerThread::onFirstRef() +// prepareTracks_l() must be called with ThreadBase::mLock held +size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) { - const size_t SIZE = 256; - char buffer[SIZE]; - snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); + size_t enabledTracks = 0; + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; - run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); -} + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); -// MixerThread::createTrack_l() must be called with AudioFlinger::mLock held -sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::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; - - // 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; - } + // 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()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t 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 = 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; + } - if (mSampleRate == 0) { - LOGE("Audio driver not initialized."); - lStatus = NO_INIT; - goto Exit; + 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; + enabledTracks++; + } 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. + 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); + } + // For tracks using static shared memory buffer, make sure that we have + // written enough data to audio hardware before disabling the track + // NOTE: this condition with arrive before track->mRetryCount <= 0 so we + // don't care about code removing track from active list above. + if ((track->mSharedBuffer == 0) || (mBytesWritten >= mMinBytesToWrite)) { + mAudioMixer->disable(AudioMixer::MIXING); + } else { + enabledTracks++; + } + } + } } - track = new Track(this, client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer); - if (track->getCblk() == NULL) { - lStatus = NO_MEMORY; - goto Exit; + // 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); + } + } } - mTracks.add(track); - lStatus = NO_ERROR; -Exit: - if(status) { - *status = lStatus; - } - return track; + return enabledTracks; } -// getTracks_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::getTracks_l( +void AudioFlinger::MixerThread::getTracks( SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks) + 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(); - LOGV ("MixerThread::getTracks_l() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; - if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + 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); - } + activeTracks.add(t); + } } } } size = activeTracks.size(); for (size_t i = 0; i < size; i++) { - removeActiveTrack_l(activeTracks[i]); + mActiveTracks.remove(activeTracks[i]); } - + size = tracks.size(); for (size_t i = 0; i < size; i++) { sp<Track> t = tracks[i]; @@ -1346,219 +1497,603 @@ void AudioFlinger::MixerThread::getTracks_l( } } -// putTracks_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::putTracks_l( +void AudioFlinger::MixerThread::putTracks( SortedVector < sp<Track> >& tracks, SortedVector < wp<Track> >& activeTracks) { - - LOGV ("MixerThread::putTracks_l() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); - + 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->mMixerThread = this; + t->mThread = this; mTracks.add(t); int j = activeTracks.indexOf(t); if (j >= 0) { - addActiveTrack_l(t); - } + mActiveTracks.add(t); + // force buffer refilling and no ramp volume when the track is mixed for the first time + t->mFillingUpStatus = Track::FS_FILLING; + } } } -uint32_t AudioFlinger::MixerThread::sampleRate() const +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::MixerThread::getTrackName_l() { - return mSampleRate; + return mAudioMixer->getTrackName(); } -int AudioFlinger::MixerThread::channelCount() const +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::MixerThread::deleteTrackName_l(int name) { - return mChannelCount; + mAudioMixer->deleteTrackName(name); } -int AudioFlinger::MixerThread::format() const +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::MixerThread::checkForNewParameters_l() { - return mFormat; -} + bool reconfig = false; -size_t AudioFlinger::MixerThread::frameCount() const -{ - return mFrameCount; -} + while (!mNewParameters.isEmpty()) { + status_t status = NO_ERROR; + String8 keyValuePair = mNewParameters[0]; + AudioParameter param = AudioParameter(keyValuePair); + int value; -uint32_t AudioFlinger::MixerThread::latency() const -{ - if (mOutput) { - return mOutput->latency(); - } - else { - return 0; + 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::setMasterVolume(float value) +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) { - mMasterVolume = value; - return NO_ERROR; -} + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; -status_t AudioFlinger::MixerThread::setMasterMute(bool muted) -{ - mMasterMute = muted; + 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; } -float AudioFlinger::MixerThread::masterVolume() const +uint32_t AudioFlinger::MixerThread::getMaxBufferRecoveryInUsecs() { - return mMasterVolume; + uint32_t time = ((mFrameCount * 1000) / mSampleRate) * 1000; + // Add some margin with regard to scheduling precision + if (time > 10000) { + time -= 10000; + } + return time; } -bool AudioFlinger::MixerThread::masterMute() const +// ---------------------------------------------------------------------------- +AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : PlaybackThread(audioFlinger, output), + mLeftVolume (1.0), mRightVolume(1.0) { - return mMasterMute; + mType = PlaybackThread::DIRECT; } -status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +AudioFlinger::DirectOutputThread::~DirectOutputThread() { - mStreamTypes[stream].volume = value; - return NO_ERROR; } -status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) + +bool AudioFlinger::DirectOutputThread::threadLoop() { - mStreamTypes[stream].mute = muted; - return NO_ERROR; + uint32_t sleepTime = 1000; + uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + sp<Track> trackToRemove; + sp<Track> activeTrack; + nsecs_t standbyTime = systemTime(); + int8_t *curBuf; + size_t mixBufferSize = mFrameCount*mFrameSize; + + while (!exitPending()) + { + processConfigEvents(); + + { // scope for the mLock + + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + } + + // 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() + kStandbyTimeInNsecs; + sleepTime = 1000; + 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()) + { + //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 = kMaxTrackRetries; + activeTrack = t; + } 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; + } + + // For tracks using static shared memry buffer, make sure that we have + // written enough data to audio hardware before disabling the track + // NOTE: this condition with arrive before track->mRetryCount <= 0 so we + // don't care about code removing track from active list above. + if ((track->mSharedBuffer != 0) && (mBytesWritten < mMinBytesToWrite)) { + activeTrack = t; + } + } + } + } + + // 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 (activeTrack != 0) { + 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() + kStandbyTimeInNsecs; + } else { + if (sleepTime == 0) { + sleepTime = maxBufferRecoveryInUsecs; + } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) { + memset (mMixBuffer, 0, mFrameCount * mFrameSize); + sleepTime = 0; + } + } + + if (mSuspended) { + sleepTime = maxBufferRecoveryInUsecs; + } + // sleepTime == 0 means we must write to audio hardware + if (sleepTime == 0) { + mLastWriteTime = systemTime(); + mInWrite = true; + int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); + if (bytesWritten) mBytesWritten += bytesWritten; + 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; } -float AudioFlinger::MixerThread::streamVolume(int stream) const +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::DirectOutputThread::getTrackName_l() { - return mStreamTypes[stream].volume; + return 0; } -bool AudioFlinger::MixerThread::streamMute(int stream) const +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name) { - return mStreamTypes[stream].mute; } -// isMusicActive_l() must be called with AudioFlinger::mLock held -bool AudioFlinger::MixerThread::isMusicActive_l() const +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() { - 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->mStreamType == AudioSystem::MUSIC) - return true; + 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 false; + return reconfig; } -// addTrack_l() must be called with AudioFlinger::mLock held -status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track) +uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs() { - status_t status = ALREADY_EXISTS; - - // here the track could be either new, or restarted - // in both cases "unstop" the track - if (track->isPaused()) { - track->mState = TrackBase::RESUMING; - LOGV("PAUSED => RESUMING (%d)", track->name()); + uint32_t time; + if (AudioSystem::isLinearPCM(mFormat)) { + time = ((mFrameCount * 1000) / mSampleRate) * 1000; + // Add some margin with regard to scheduling precision + if (time > 10000) { + time -= 10000; + } } else { - track->mState = TrackBase::ACTIVE; - LOGV("? => ACTIVE (%d)", track->name()); - } - // 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; - addActiveTrack_l(track); - status = NO_ERROR; + time = 10000; } - - LOGV("mWaitWorkCV.broadcast"); - mAudioFlinger->mWaitWorkCV.broadcast(); - - return status; + return time; } -// destroyTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track) +// ---------------------------------------------------------------------------- + +AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread) + : MixerThread(audioFlinger, mainThread->getOutput()) { - track->mState = TrackBase::TERMINATED; - if (mActiveTracks.indexOf(track) < 0) { - LOGV("remove track (%d) and delete from mixer", track->name()); - mTracks.remove(track); - deleteTrackName_l(track->name()); - } + mType = PlaybackThread::DUPLICATING; + addOutputTrack(mainThread); } -// addActiveTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::addActiveTrack_l(const wp<Track>& t) +AudioFlinger::DuplicatingThread::~DuplicatingThread() { - mActiveTracks.add(t); - - // Force routing to speaker for certain stream types - // The forced routing to speaker is managed by hardware mixer - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - sp<Track> track = t.promote(); - if (track == NULL) return; - - if (streamForcedToSpeaker(track->type())) { - mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); - } - } + mOutputTracks.clear(); } -// removeActiveTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::removeActiveTrack_l(const wp<Track>& t) +bool AudioFlinger::DuplicatingThread::threadLoop() { - mActiveTracks.remove(t); + uint32_t sleepTime = 1000; + uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mFrameSize; + SortedVector< sp<OutputTrack> > outputTracks; + uint32_t writeFrames = 0; + + while (!exitPending()) + { + processConfigEvents(); + + enabledTracks = 0; + { // scope for the mLock - // Force routing to speaker for certain stream types - // The forced routing to speaker is managed by hardware mixer - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - sp<Track> track = t.promote(); - if (track == NULL) return; + Mutex::Autolock _l(mLock); - if (streamForcedToSpeaker(track->type())) { - mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs(); + } + + 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 = 1000; + continue; + } + } + + enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); } + + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + sleepTime = 0; + writeFrames = mFrameCount; + } else { + if (sleepTime == 0) { + sleepTime = maxBufferRecoveryInUsecs; + } 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 = maxBufferRecoveryInUsecs; + } + // 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(); } -} -// getTrackName_l() must be called with AudioFlinger::mLock held -int AudioFlinger::MixerThread::getTrackName_l() -{ - return mAudioMixer->getTrackName(); + { // scope for the mLock + + Mutex::Autolock _l(mLock); + if (!mStandby) { + LOGV("DuplicatingThread() exiting out of standby"); + for (size_t i = 0; i < mOutputTracks.size(); i++) { + mOutputTracks[i]->destroy(); + } + } + } + + return false; } -// deleteTrackName_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::deleteTrackName_l(int name) +void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { - mAudioMixer->deleteTrackName(name); + int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); + OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, + 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); + } } -size_t AudioFlinger::MixerThread::getOutputFrameCount() +void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) { - return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); + 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); + return; + } + } + LOGV("removeOutputTrack(): unkonwn thread: %p", thread); } // ---------------------------------------------------------------------------- // TrackBase constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::TrackBase::TrackBase( - const sp<MixerThread>& mixerThread, +AudioFlinger::ThreadBase::TrackBase::TrackBase( + const wp<ThreadBase>& thread, const sp<Client>& client, uint32_t sampleRate, int format, @@ -1567,21 +2102,15 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( uint32_t flags, const sp<IMemory>& sharedBuffer) : RefBase(), - mMixerThread(mixerThread), + mThread(thread), mClient(client), + mCblk(0), mFrameCount(0), mState(IDLE), mClientTid(-1), mFormat(format), mFlags(flags & ~SYSTEM_FLAGS_MASK) { - mName = mixerThread->getTrackName_l(); - LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - if (mName < 0) { - LOGE("no more track names availlable"); - return; - } - LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); @@ -1635,16 +2164,22 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( } } -AudioFlinger::MixerThread::TrackBase::~TrackBase() +AudioFlinger::ThreadBase::TrackBase::~TrackBase() { if (mCblk) { - mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + if (mClient == NULL) { + delete mCblk; + } } mCblkMemory.clear(); // and free the shared memory - mClient.clear(); + if (mClient != NULL) { + Mutex::Autolock _l(mClient->audioFlinger()->mLock); + mClient.clear(); + } } -void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { buffer->raw = 0; mFrameCount = buffer->frameCount; @@ -1652,7 +2187,7 @@ void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Bu buffer->frameCount = 0; } -bool AudioFlinger::MixerThread::TrackBase::step() { +bool AudioFlinger::ThreadBase::TrackBase::step() { bool result; audio_track_cblk_t* cblk = this->cblk(); @@ -1664,7 +2199,7 @@ bool AudioFlinger::MixerThread::TrackBase::step() { return result; } -void AudioFlinger::MixerThread::TrackBase::reset() { +void AudioFlinger::ThreadBase::TrackBase::reset() { audio_track_cblk_t* cblk = this->cblk(); cblk->user = 0; @@ -1675,27 +2210,27 @@ void AudioFlinger::MixerThread::TrackBase::reset() { LOGV("TrackBase::reset"); } -sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const +sp<IMemory> AudioFlinger::ThreadBase::TrackBase::getCblk() const { return mCblkMemory; } -int AudioFlinger::MixerThread::TrackBase::sampleRate() const { +int AudioFlinger::ThreadBase::TrackBase::sampleRate() const { return (int)mCblk->sampleRate; } -int AudioFlinger::MixerThread::TrackBase::channelCount() const { +int AudioFlinger::ThreadBase::TrackBase::channelCount() const { return (int)mCblk->channels; } -void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { +void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { audio_track_cblk_t* cblk = this->cblk(); - int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; - int16_t *bufferEnd = bufferStart + frames * cblk->channels; + 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 || - (cblk->channels == 2 && ((unsigned long)bufferStart & 3))) { + 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, @@ -1708,9 +2243,9 @@ void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t // ---------------------------------------------------------------------------- -// Track constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::Track::Track( - const sp<MixerThread>& mixerThread, +// 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, @@ -1718,42 +2253,62 @@ AudioFlinger::MixerThread::Track::Track( int channelCount, int frameCount, const sp<IMemory>& sharedBuffer) - : TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) -{ - mVolume[0] = 1.0f; - mVolume[1] = 1.0f; - mMute = false; - mSharedBuffer = sharedBuffer; - mStreamType = streamType; + : 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::MixerThread::Track::~Track() +AudioFlinger::PlaybackThread::Track::~Track() { - wp<Track> weak(this); // never create a strong ref from the dtor - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mState = TERMINATED; + LOGV("PlaybackThread::Track destructor"); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + mState = TERMINATED; + } } -void AudioFlinger::MixerThread::Track::destroy() +void AudioFlinger::PlaybackThread::Track::destroy() { - // NOTE: destroyTrack_l() can remove a strong reference to this Track + // 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 AudioFlinger::mLock, - // we must acquire a strong reference on this Track before locking AudioFlinger::mLock + // 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 + // 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 AudioFlinger::mLock - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->destroyTrack_l(this); + { // scope for mLock + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->destroyTrack_l(this); + } } } -void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) +void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) { - snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", + 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, @@ -1770,7 +2325,7 @@ void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) mCblk->user); } -status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesReady; @@ -1807,76 +2362,90 @@ status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Bu 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::MixerThread::Track::isReady() const { +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; - LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); return true; } return false; } -status_t AudioFlinger::MixerThread::Track::start() +status_t AudioFlinger::PlaybackThread::Track::start() { - LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->addTrack_l(this); + LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->addTrack_l(this); + } return NO_ERROR; } -void AudioFlinger::MixerThread::Track::stop() +void AudioFlinger::PlaybackThread::Track::stop() { - LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState > STOPPED) { - mState = STOPPED; - // If the track is not active (PAUSED and buffers full), flush buffers - if (mMixerThread->mActiveTracks.indexOf(this) < 0) { - reset(); + LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + 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); } - LOGV("(> STOPPED) => STOPPED (%d)", mName); } } -void AudioFlinger::MixerThread::Track::pause() +void AudioFlinger::PlaybackThread::Track::pause() { LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState == ACTIVE || mState == RESUMING) { - mState = PAUSING; - LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + 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()); + } } } -void AudioFlinger::MixerThread::Track::flush() +void AudioFlinger::PlaybackThread::Track::flush() { LOGV("flush(%d)", mName); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { - return; - } - // No point remaining in PAUSED state after a flush => go to - // STOPPED state - mState = STOPPED; + 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(); + 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::MixerThread::Track::reset() +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. @@ -1886,17 +2455,17 @@ void AudioFlinger::MixerThread::Track::reset() // written to buffer mCblk->flowControlFlag = 1; mCblk->forceReady = 0; - mFillingUpStatus = FS_FILLING; + mFillingUpStatus = FS_FILLING; mResetDone = true; } } -void AudioFlinger::MixerThread::Track::mute(bool muted) +void AudioFlinger::PlaybackThread::Track::mute(bool muted) { mMute = muted; } -void AudioFlinger::MixerThread::Track::setVolume(float left, float right) +void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right) { mVolume[0] = left; mVolume[1] = right; @@ -1905,28 +2474,35 @@ void AudioFlinger::MixerThread::Track::setVolume(float left, float right) // ---------------------------------------------------------------------------- // RecordTrack constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::RecordTrack::RecordTrack( - const sp<MixerThread>& mixerThread, +AudioFlinger::RecordThread::RecordTrack::RecordTrack( + const wp<ThreadBase>& thread, const sp<Client>& client, - int inputSource, uint32_t sampleRate, int format, int channelCount, int frameCount, uint32_t flags) - : TrackBase(mixerThread, client, sampleRate, format, + : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, flags, 0), - mOverflow(false), mInputSource(inputSource) -{ + 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::MixerThread::RecordTrack::~RecordTrack() +AudioFlinger::RecordThread::RecordTrack::~RecordTrack() { - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->deleteTrackName_l(mName); } -status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesAvail; @@ -1965,180 +2541,247 @@ getNextBuffer_exit: return NOT_ENOUGH_DATA; } -status_t AudioFlinger::MixerThread::RecordTrack::start() +status_t AudioFlinger::RecordThread::RecordTrack::start() +{ + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + return recordThread->start(this); + } + return NO_INIT; +} + +void AudioFlinger::RecordThread::RecordTrack::stop() { - return mMixerThread->mAudioFlinger->startRecord(this); + 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::MixerThread::RecordTrack::stop() +void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) { - mMixerThread->mAudioFlinger->stopRecord(this); - TrackBase::reset(); - // Force overerrun condition to avoid false overrun callback until first data is - // read from buffer - mCblk->flowControlFlag = 1; + 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::MixerThread::OutputTrack::OutputTrack( - const sp<MixerThread>& mixerThread, +AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( + const wp<ThreadBase>& thread, uint32_t sampleRate, int format, int channelCount, int frameCount) - : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), - mOutputMixerThread(mixerThread) + : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL), + mActive(false) { - - mCblk->out = 1; - mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); - mCblk->volume[0] = mCblk->volume[1] = 0x1000; - mOutBuffer.frameCount = 0; - mCblk->bufferTimeoutMs = 10; - - 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); - + + 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; + mWaitTimeMs = (playbackThread->frameCount() * 2 * 1000) / playbackThread->sampleRate(); + 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 mWaitTimeMs %d", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd, mWaitTimeMs); + } else { + LOGW("Error creating output track on thread %p", playbackThread); + } } -AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack() { - stop(); + clearBufferQueue(); } -status_t AudioFlinger::MixerThread::OutputTrack::start() +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::MixerThread::OutputTrack::stop() +void AudioFlinger::PlaybackThread::OutputTrack::stop() { Track::stop(); clearBufferQueue(); mOutBuffer.frameCount = 0; + mActive = false; } -void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +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; - - if (mCblk->user == 0) { - mOutputMixerThread->mAudioFlinger->mLock.lock(); - bool isMusicActive = mOutputMixerThread->isMusicActive_l(); - mOutputMixerThread->mAudioFlinger->mLock.unlock(); - if (isMusicActive) { - mCblk->forceReady = 1; - LOGV("OutputTrack::start() force ready"); - } else if (mCblk->frameCount > frames){ - if (mBufferQueue.size() < kMaxOutputTrackBuffers) { - uint32_t startFrames = (mCblk->frameCount - frames); - LOGV("OutputTrack::start() write %d frames", startFrames); - 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() no more buffers"); + + uint32_t waitTimeLeftMs = mWaitTimeMs; + + 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 (1) { + 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; - if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + nsecs_t startTime = systemTime(); + if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + LOGV ("OutputTrack::write() %p no more output buffers", this); + outputBufferFull = true; break; } + uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime); + LOGV("OutputTrack::write() to thread %p waitTimeMs %d waitTimeLeftMs %d", mThread.unsafe_get(), waitTimeMs, waitTimeLeftMs); + 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; - + mOutBuffer.i16 += outFrames * channels; + if (pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { mBufferQueue.removeAt(0); delete [] pInBuffer->mBuffer; delete pInBuffer; + LOGV("OutputTrack::write() %p released overflow buffer %d", this, mBufferQueue.size()); } else { break; } } } - + // If we could not write all frames, allocate a buffer and queue it for next time. if (inBuffer.frameCount) { - if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + 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 adding overflow buffer %d", this, mBufferQueue.size()); } else { - LOGW("OutputTrack::write() no more buffers"); + LOGW("OutputTrack::write() %p no more overflow buffers", 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 + // 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 && 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); + 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::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs) { int active; - int timeout = 0; 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); +// LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); buffer->frameCount = 0; - + uint32_t framesAvail = cblk->framesAvailable(); + if (framesAvail == 0) { - return AudioTrack::NO_MORE_BUFFERS; + 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; } @@ -2156,11 +2799,11 @@ status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvide } -void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +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; @@ -2180,9 +2823,10 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t 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(mPid); + mAudioFlinger->removeClient_l(mPid); } const sp<MemoryDealer>& AudioFlinger::Client::heap() const @@ -2192,7 +2836,7 @@ const sp<MemoryDealer>& AudioFlinger::Client::heap() const // ---------------------------------------------------------------------------- -AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track) +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) : BnAudioTrack(), mTrack(track) { @@ -2244,7 +2888,7 @@ status_t AudioFlinger::TrackHandle::onTransact( sp<IAudioRecord> AudioFlinger::openRecord( pid_t pid, - int inputSource, + int input, uint32_t sampleRate, int format, int channelCount, @@ -2252,14 +2896,13 @@ sp<IAudioRecord> AudioFlinger::openRecord( uint32_t flags, status_t *status) { - sp<MixerThread::RecordTrack> recordTrack; + sp<RecordThread::RecordTrack> recordTrack; sp<RecordHandle> recordHandle; sp<Client> client; wp<Client> wclient; - AudioStreamIn* input = 0; - int inFrameCount; - size_t inputBufferSize; status_t lStatus; + RecordThread *thread; + size_t inFrameCount; // check calling permissions if (!recordingAllowed()) { @@ -2267,30 +2910,15 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) { - LOGE("invalid stream type"); - lStatus = BAD_VALUE; - goto Exit; - } - - if (mAudioRecordThread == 0) { - LOGE("Audio record thread not started"); - lStatus = NO_INIT; - goto Exit; - } - - - // Check that audio input stream accepts requested audio parameters - inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); - if (inputBufferSize == 0) { - lStatus = BAD_VALUE; - LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount); - 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(); @@ -2299,15 +2927,14 @@ sp<IAudioRecord> AudioFlinger::openRecord( mClients.add(pid, client); } - // frameCount must be a multiple of input buffer size - inFrameCount = inputBufferSize/channelCount/sizeof(short); - frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; - // create new record track. The record track uses one track in mHardwareMixerThread by convention. - recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate, + 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; @@ -2324,22 +2951,9 @@ Exit: return recordHandle; } -status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { - if (mAudioRecordThread != 0) { - return mAudioRecordThread->start(recordTrack); - } - return NO_INIT; -} - -void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { - if (mAudioRecordThread != 0) { - mAudioRecordThread->stop(recordTrack); - } -} - // ---------------------------------------------------------------------------- -AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack) +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack) : BnAudioRecord(), mRecordTrack(recordTrack) { @@ -2371,86 +2985,164 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware, - const sp<AudioFlinger>& audioFlinger) : - mAudioHardware(audioHardware), - mAudioFlinger(audioFlinger), - mActive(false) +AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels) : + ThreadBase(audioFlinger), + mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0) { + mReqChannelCount = AudioSystem::popCount(channels); + mReqSampleRate = sampleRate; + readInputParameters(); + sendConfigEvent(AudioSystem::INPUT_OPENED); } -AudioFlinger::AudioRecordThread::~AudioRecordThread() + +AudioFlinger::RecordThread::~RecordThread() { + delete[] mRsmpInBuffer; + if (mResampler != 0) { + delete mResampler; + delete[] mRsmpOutBuffer; + } } -bool AudioFlinger::AudioRecordThread::threadLoop() +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() { - LOGV("AudioRecordThread: start record loop"); AudioBufferProvider::Buffer buffer; - int inBufferSize = 0; - int inFrameCount = 0; - AudioStreamIn* input = 0; + sp<RecordTrack> activeTrack; - mActive = 0; - // start recording while (!exitPending()) { - if (!mActive) { - mLock.lock(); - if (!mActive && !exitPending()) { - LOGV("AudioRecordThread: loop stopping"); - if (input) { - delete input; - input = 0; + + processConfigEvents(); + + { // scope for mLock + Mutex::Autolock _l(mLock); + checkForNewParameters_l(); + if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { + if (!mStandby) { + mInput->standby(); + mStandby = true; } - mRecordTrack.clear(); - mStopped.signal(); + if (exitPending()) break; + + LOGV("RecordThread: loop stopping"); + // go to sleep mWaitWorkCV.wait(mLock); - - LOGV("AudioRecordThread: loop starting"); - if (mRecordTrack != 0) { - input = mAudioHardware->openInputStream( - mRecordTrack->inputSource(), - mRecordTrack->format(), - mRecordTrack->channelCount(), - mRecordTrack->sampleRate(), - &mStartStatus, - (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16)); - if (input != 0) { - inBufferSize = input->bufferSize(); - inFrameCount = inBufferSize/input->frameSize(); + LOGV("RecordThread: loop starting"); + continue; + } + if (mActiveTrack != 0) { + if (mActiveTrack->mState == TrackBase::PAUSING) { + mActiveTrack.clear(); + mStartStopCond.broadcast(); + } else if (mActiveTrack->mState == TrackBase::RESUMING) { + mRsmpInIndex = mFrameCount; + if (mReqChannelCount != mActiveTrack->channelCount()) { + mActiveTrack.clear(); + } else { + mActiveTrack->mState = TrackBase::ACTIVE; } - } else { - mStartStatus = NO_INIT; - } - if (mStartStatus !=NO_ERROR) { - LOGW("record start failed, status %d", mStartStatus); - mActive = false; - mRecordTrack.clear(); + mStartStopCond.broadcast(); } - mWaitWorkCV.signal(); + mStandby = false; } - mLock.unlock(); - } else if (mRecordTrack != 0) { - - buffer.frameCount = inFrameCount; - if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR && - (int)buffer.frameCount == inFrameCount)) { - LOGV("AudioRecordThread read: %d frames", buffer.frameCount); - ssize_t bytesRead = input->read(buffer.raw, inBufferSize); - if (bytesRead < 0) { - LOGE("Error reading audio input"); - sleep(1); + } + + if (mActiveTrack != 0) { + 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) { + ssize_t bytesRead; + if (framesOut == mFrameCount && + (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { + bytesRead = mInput->read(buffer.raw, mInputBytes); + framesOut = 0; + } else { + bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + mRsmpInIndex = 0; + } + if (bytesRead < 0) { + LOGE("Error reading audio input"); + sleep(1); + 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); + } + } - mRecordTrack->releaseBuffer(&buffer); - mRecordTrack->overflow(); + mActiveTrack->releaseBuffer(&buffer); + mActiveTrack->overflow(); } - // client isn't retrieving buffers fast enough else { - if (!mRecordTrack->setOverflow()) - LOGW("AudioRecordThread: buffer overflow"); + 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. @@ -2459,73 +3151,559 @@ bool AudioFlinger::AudioRecordThread::threadLoop() } } - - if (input) { - delete input; + if (!mStandby) { + mInput->standby(); } - mRecordTrack.clear(); - + mActiveTrack.clear(); + + LOGV("RecordThread %p exiting", this); return false; } -status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) +status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack) { - LOGV("AudioRecordThread::start"); + LOGV("RecordThread::start"); AutoMutex lock(&mLock); - mActive = true; - // If starting the active track, just reset mActive in case a stop - // was pending and exit - if (recordTrack == mRecordTrack.get()) return NO_ERROR; - if (mRecordTrack != 0) return -EBUSY; + if (mActiveTrack != 0) { + if (recordTrack != mActiveTrack.get()) return -EBUSY; + + if (mActiveTrack->mState == TrackBase::PAUSING) mActiveTrack->mState = TrackBase::RESUMING; - mRecordTrack = recordTrack; + return NO_ERROR; + } + mActiveTrack = recordTrack; + mActiveTrack->mState = TrackBase::RESUMING; // signal thread to start LOGV("Signal record thread"); mWaitWorkCV.signal(); - mWaitWorkCV.wait(mLock); - LOGV("Record started, status %d", mStartStatus); - return mStartStatus; -} - -void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { - LOGV("AudioRecordThread::stop"); - AutoMutex lock(&mLock); - if (mActive && (recordTrack == mRecordTrack.get())) { - mActive = false; - mStopped.wait(mLock); + mStartStopCond.wait(mLock); + if (mActiveTrack != 0) { + LOGV("Record started OK"); + return NO_ERROR; + } else { + LOGV("Record failed to start"); + return BAD_VALUE; } } -void AudioFlinger::AudioRecordThread::exit() -{ - LOGV("AudioRecordThread::exit"); - { - AutoMutex lock(&mLock); - requestExit(); - mWaitWorkCV.signal(); +void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { + LOGV("RecordThread::stop"); + AutoMutex lock(&mLock); + if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { + mActiveTrack->mState = TrackBase::PAUSING; + mStartStopCond.wait(mLock); } - requestExitAndWait(); } -status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& args) +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; - if (mRecordTrack != 0 && mRecordTrack->mClient != 0) { - snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid()); + 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) { + ssize_t bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + if (bytesRead < 0) { + LOGE("RecordThread::getNextBuffer() Error reading audio input"); + sleep(1); + 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, this, 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; +} + +// ---------------------------------------------------------------------------- + +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); + LOGV("openOutput() created direct output: ID %d thread %p", (mNextThreadId + 1), thread); + } else { + thread = new MixerThread(this, output); + LOGV("openOutput() created mixer output: ID %d thread %p", (mNextThreadId + 1), thread); + } + mPlaybackThreads.add(++mNextThreadId, thread); + + if (pSamplingRate) *pSamplingRate = samplingRate; + if (pFormat) *pFormat = format; + if (pChannels) *pChannels = channels; + if (pLatencyMs) *pLatencyMs = thread->latency(); + } + + return mNextThreadId; +} + +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); + 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, thread, 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); + 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; +} + +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, thread, 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) { @@ -2533,6 +3711,7 @@ status_t AudioFlinger::onTransact( } // ---------------------------------------------------------------------------- + void AudioFlinger::instantiate() { defaultServiceManager()->addService( String16("media.audio_flinger"), new AudioFlinger()); diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 634934e..22d15c9 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -30,8 +30,7 @@ #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/MemoryDealer.h> -#include <utils/KeyedVector.h> +#include <binder/MemoryDealer.h> #include <utils/SortedVector.h> #include <utils/Vector.h> @@ -44,6 +43,7 @@ namespace android { class audio_track_cblk_t; class AudioMixer; class AudioBuffer; +class AudioResampler; // ---------------------------------------------------------------------------- @@ -56,7 +56,7 @@ class AudioBuffer; static const nsecs_t kStandbyTimeInNsecs = seconds(3); -class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient { public: static void instantiate(); @@ -73,6 +73,7 @@ public: int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, + int output, status_t *status); virtual uint32_t sampleRate(int output) const; @@ -87,33 +88,53 @@ public: virtual float masterVolume() const; virtual bool masterMute() const; - virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamVolume(int stream, float value, int output); virtual status_t setStreamMute(int stream, bool muted); - virtual float streamVolume(int stream) const; + virtual float streamVolume(int stream, int output) const; virtual bool streamMute(int stream) const; - virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask); - virtual uint32_t getRouting(int mode) const; - virtual status_t setMode(int mode); - virtual int getMode() const; virtual status_t setMicMute(bool state); virtual bool getMicMute() const; virtual bool isMusicActive() const; - virtual bool isA2dpEnabled() const; - - virtual status_t setParameter(const char* key, const char* value); + 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 void wakeUp() { mWaitWorkCV.broadcast(); } - + + 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); + // IBinder::DeathRecipient virtual void binderDied(const wp<IBinder>& who); @@ -139,7 +160,7 @@ public: // record interface virtual sp<IAudioRecord> openRecord( pid_t pid, - int inputSource, + int input, uint32_t sampleRate, int format, int channelCount, @@ -156,27 +177,7 @@ public: private: AudioFlinger(); virtual ~AudioFlinger(); - - void setOutput(int outputType); - void doSetOutput(int outputType); - -#ifdef WITH_A2DP - void setA2dpEnabled_l(bool enable); - void checkA2dpEnabledChange_l(); -#endif - static bool streamForcedToSpeaker(int streamType); - - // Management of forced route to speaker for certain track types. - enum force_speaker_command { - ACTIVE_TRACK_ADDED = 0, - ACTIVE_TRACK_REMOVED, - CHECK_ROUTE_RESTORE_TIME, - FORCE_ROUTE_RESTORE - }; - void handleForcedSpeakerRoute(int command); -#ifdef WITH_A2DP - void handleRouteDisablesA2dp_l(int routes); -#endif + // Internal dump utilites. status_t dumpPermissionDenial(int fd, const Vector<String16>& args); @@ -190,6 +191,8 @@ private: 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&); @@ -201,14 +204,19 @@ private: class TrackHandle; class RecordHandle; - class AudioRecordThread; - - - // --- MixerThread --- - class MixerThread : public Thread { + class RecordThread; + class PlaybackThread; + class MixerThread; + class DirectOutputThread; + class Track; + class RecordTrack; + + class ThreadBase : public Thread { public: - - // --- Track --- + ThreadBase (const sp<AudioFlinger>& audioFlinger); + virtual ~ThreadBase(); + + status_t dumpBase(int fd, const Vector<String16>& args); // base for record and playback class TrackBase : public AudioBufferProvider, public RefBase { @@ -230,7 +238,7 @@ private: // The upper 16 bits are used for track-specific flags. }; - TrackBase(const sp<MixerThread>& mixerThread, + TrackBase(const wp<ThreadBase>& thread, const sp<Client>& client, uint32_t sampleRate, int format, @@ -243,11 +251,15 @@ private: virtual status_t start() = 0; virtual void stop() = 0; sp<IMemory> getCblk() const; + audio_track_cblk_t* cblk() const { return mCblk; } protected: - friend class MixerThread; + friend class ThreadBase; friend class RecordHandle; - friend class AudioRecordThread; + friend class PlaybackThread; + friend class RecordThread; + friend class MixerThread; + friend class DirectOutputThread; TrackBase(const TrackBase&); TrackBase& operator = (const TrackBase&); @@ -255,10 +267,6 @@ private: virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); - audio_track_cblk_t* cblk() const { - return mCblk; - } - int format() const { return mFormat; } @@ -269,10 +277,6 @@ private: void* getBuffer(uint32_t offset, uint32_t frames) const; - int name() const { - return mName; - } - bool isStopped() const { return mState == STOPPED; } @@ -284,14 +288,13 @@ private: bool step(); void reset(); - sp<MixerThread> mMixerThread; + wp<ThreadBase> mThread; sp<Client> mClient; sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; void* mBuffer; void* mBufferEnd; uint32_t mFrameCount; - int mName; // we don't really need a lock for these int mState; int mClientTid; @@ -299,10 +302,69 @@ private: 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(); + + 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; + }; + + // --- PlaybackThread --- + class PlaybackThread : public ThreadBase { + public: + + enum type { + MIXER, + DIRECT, + DUPLICATING + }; + // playback track class Track : public TrackBase { public: - Track( const sp<MixerThread>& mixerThread, + Track( const wp<ThreadBase>& thread, const sp<Client>& client, int streamType, uint32_t sampleRate, @@ -321,6 +383,9 @@ private: void destroy(); void mute(bool); void setVolume(float left, float right); + int name() const { + return mName; + } int type() const { return mStreamType; @@ -328,29 +393,25 @@ private: protected: - friend class MixerThread; + friend class ThreadBase; friend class AudioFlinger; - friend class AudioFlinger::TrackHandle; + 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() const { - return (mMute || mMixerThread->mStreamTypes[mStreamType].mute); - } - + 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(); @@ -364,54 +425,20 @@ private: sp<IMemory> mSharedBuffer; bool mResetDone; int mStreamType; + int mName; }; // end of Track - // record track - class RecordTrack : public TrackBase { - public: - RecordTrack(const sp<MixerThread>& mixerThread, - const sp<Client>& client, - int inputSource, - 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; } - - int inputSource() const { return mInputSource; } - - private: - friend class AudioFlinger; - friend class AudioFlinger::RecordHandle; - friend class AudioFlinger::AudioRecordThread; - friend class MixerThread; - - RecordTrack(const Track&); - RecordTrack& operator = (const Track&); - - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - - bool mOverflow; - int mInputSource; - }; // playback track class OutputTrack : public Track { public: - + class Buffer: public AudioBufferProvider::Buffer { public: int16_t *mBuffer; }; - - OutputTrack( const sp<MixerThread>& mixerThread, + + OutputTrack( const wp<ThreadBase>& thread, uint32_t sampleRate, int format, int channelCount, @@ -420,35 +447,35 @@ private: virtual status_t start(); virtual void stop(); - void write(int16_t* data, uint32_t frames); + 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); + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs); void clearBufferQueue(); - - sp<MixerThread> mOutputMixerThread; + + // Maximum number of pending buffers allocated by OutputTrack::write() + static const uint8_t kMaxOverFlowBuffers = 3; + Vector < Buffer* > mBufferQueue; AudioBufferProvider::Buffer mOutBuffer; - uint32_t mFramesWritten; - - }; // end of OutputTrack + uint32_t mWaitTimeMs; + bool mActive; - MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType); - virtual ~MixerThread(); + }; // end of OutputTrack + + PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + virtual ~PlaybackThread(); virtual status_t dump(int fd, const Vector<String16>& args); // Thread virtuals - virtual bool threadLoop(); virtual status_t readyToRun(); virtual void onFirstRef(); - virtual uint32_t sampleRate() const; - virtual int channelCount() const; - virtual int format() const; - virtual size_t frameCount() const; virtual uint32_t latency() const; virtual status_t setMasterVolume(float value); @@ -463,9 +490,8 @@ private: virtual float streamVolume(int stream) const; virtual bool streamMute(int stream) const; - bool isMusicActive_l() const; - - + bool isMusicActive() const; + sp<Track> createTrack_l( const sp<AudioFlinger::Client>& client, int streamType, @@ -475,13 +501,15 @@ private: int frameCount, const sp<IMemory>& sharedBuffer, status_t *status); - - void getTracks_l(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); - void putTracks_l(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); - void setOuputTrack(OutputTrack *track) { mOutputTrack = track; } - + + AudioStreamOut* getOutput() { return mOutput; } + + virtual int type() const { return mType; } + void suspend() { mSuspended++; } + void restore() { if (mSuspended) mSuspended--; } + virtual String8 getParameters(const String8& keys); + virtual void audioConfigChanged(int event, int param = 0); + struct stream_type_t { stream_type_t() : volume(1.0f), @@ -492,56 +520,122 @@ private: bool mute; }; - private: + 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 getMaxBufferRecoveryInUsecs() = 0; + private: friend class AudioFlinger; + friend class OutputTrack; friend class Track; friend class TrackBase; - friend class RecordTrack; - - MixerThread(const Client&); - MixerThread& operator = (const MixerThread&); - + 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); - int getTrackName_l(); - void deleteTrackName_l(int name); - void addActiveTrack_l(const wp<Track>& t); - void removeActiveTrack_l(const wp<Track>& t); - size_t getOutputFrameCount(); - status_t dumpInternals(int fd, const Vector<String16>& args); + void readOutputParameters(); + + virtual status_t dumpInternals(int fd, const Vector<String16>& args); status_t dumpTracks(int fd, const Vector<String16>& args); - - sp<AudioFlinger> mAudioFlinger; - SortedVector< wp<Track> > mActiveTracks; + SortedVector< sp<Track> > mTracks; - stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; - AudioMixer* mAudioMixer; + // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1]; AudioStreamOut* mOutput; - int mOutputType; - uint32_t mSampleRate; - size_t mFrameCount; - int mChannelCount; - int mFormat; - int16_t* mMixBuffer; float mMasterVolume; - bool mMasterMute; nsecs_t mLastWriteTime; int mNumWrites; int mNumDelayedWrites; - bool mStandby; bool mInWrite; - sp <OutputTrack> mOutputTrack; + int mMinBytesToWrite; + }; + + class MixerThread : public PlaybackThread { + public: + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + 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: + size_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 getMaxBufferRecoveryInUsecs(); + + AudioMixer* mAudioMixer; + }; + + class DirectOutputThread : public PlaybackThread { + public: + + DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + ~DirectOutputThread(); + + // Thread virtuals + virtual bool threadLoop(); + + virtual bool checkForNewParameters_l(); + + protected: + virtual int getTrackName_l(); + virtual void deleteTrackName_l(int name); + virtual uint32_t getMaxBufferRecoveryInUsecs(); + + private: + float mLeftVolume; + float mRightVolume; + }; + + class DuplicatingThread : public MixerThread { + public: + DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread); + ~DuplicatingThread(); + + // Thread virtuals + virtual bool threadLoop(); + void addOutputTrack(MixerThread* thread); + void removeOutputTrack(MixerThread* thread); + + private: + SortedVector < sp<OutputTrack> > mOutputTracks; }; - + 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, const sp<ThreadBase>& thread, void *param2); + friend class AudioBuffer; class TrackHandle : public android::BnAudioTrack { public: - TrackHandle(const sp<MixerThread::Track>& track); + TrackHandle(const sp<PlaybackThread::Track>& track); virtual ~TrackHandle(); virtual status_t start(); virtual void stop(); @@ -553,20 +647,92 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<MixerThread::Track> mTrack; + sp<PlaybackThread::Track> mTrack; }; friend class Client; - friend class MixerThread::Track; + 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); + ~RecordThread(); + + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef(); - void removeClient(pid_t pid); + 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(); + 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; + }; class RecordHandle : public android::BnAudioRecord { public: - RecordHandle(const sp<MixerThread::RecordTrack>& recordTrack); + RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); virtual status_t start(); virtual void stop(); @@ -574,66 +740,31 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<MixerThread::RecordTrack> mRecordTrack; - }; - - // record thread - class AudioRecordThread : public Thread - { - public: - AudioRecordThread(AudioHardwareInterface* audioHardware, const sp<AudioFlinger>& audioFlinger); - virtual ~AudioRecordThread(); - virtual bool threadLoop(); - virtual status_t readyToRun() { return NO_ERROR; } - virtual void onFirstRef() {} - - status_t start(MixerThread::RecordTrack* recordTrack); - void stop(MixerThread::RecordTrack* recordTrack); - void exit(); - status_t dump(int fd, const Vector<String16>& args); - - private: - AudioRecordThread(); - AudioHardwareInterface *mAudioHardware; - sp<AudioFlinger> mAudioFlinger; - sp<MixerThread::RecordTrack> mRecordTrack; - Mutex mLock; - Condition mWaitWorkCV; - Condition mStopped; - volatile bool mActive; - status_t mStartStatus; + sp<RecordThread::RecordTrack> mRecordTrack; }; - friend class AudioRecordThread; - friend class MixerThread; + friend class RecordThread; + friend class PlaybackThread; - status_t startRecord(MixerThread::RecordTrack* recordTrack); - void stopRecord(MixerThread::RecordTrack* recordTrack); - mutable Mutex mHardwareLock; mutable Mutex mLock; - mutable Condition mWaitWorkCV; DefaultKeyedVector< pid_t, wp<Client> > mClients; - sp<MixerThread> mA2dpMixerThread; - sp<MixerThread> mHardwareMixerThread; + mutable Mutex mHardwareLock; AudioHardwareInterface* mAudioHardware; - AudioHardwareInterface* mA2dpAudioInterface; - sp<AudioRecordThread> mAudioRecordThread; - bool mA2dpEnabled; - bool mNotifyA2dpChange; mutable int mHardwareStatus; - SortedVector< wp<IBinder> > mNotificationClients; - int mForcedSpeakerCount; - int mA2dpDisableCount; - - // true if A2DP should resume when mA2dpDisableCount returns to zero - bool mA2dpSuppressed; - uint32_t mSavedRoute; - uint32_t mForcedRoute; - nsecs_t mRouteRestoreTime; - bool mMusicMuteSaved; + + + 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; }; // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp index 1e159b8..57874f3 100644 --- a/libs/audioflinger/AudioHardwareGeneric.cpp +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -49,8 +49,8 @@ AudioHardwareGeneric::AudioHardwareGeneric() AudioHardwareGeneric::~AudioHardwareGeneric() { if (mFd >= 0) ::close(mFd); - delete mOutput; - delete mInput; + closeOutputStream((AudioStreamOut *)mOutput); + closeInputStream((AudioStreamIn *)mInput); } status_t AudioHardwareGeneric::initCheck() @@ -63,7 +63,7 @@ status_t AudioHardwareGeneric::initCheck() } AudioStreamOut* AudioHardwareGeneric::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { AutoMutex lock(mLock); @@ -77,7 +77,7 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream( // create new output stream AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); - status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate); + status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate); if (status) { *status = lStatus; } @@ -89,17 +89,19 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream( return mOutput; } -void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { - if (out == mOutput) mOutput = 0; +void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) { + if (mOutput && out == mOutput) { + delete mOutput; + mOutput = 0; + } } AudioStreamIn* AudioHardwareGeneric::openInputStream( - int inputSource, int format, int channelCount, uint32_t sampleRate, + 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 ((inputSource < AudioRecord::DEFAULT_INPUT) || - (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { return 0; } @@ -115,7 +117,7 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream( // create new output stream AudioStreamInGeneric* in = new AudioStreamInGeneric(); - status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate, acoustics); + status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics); if (status) { *status = lStatus; } @@ -127,8 +129,11 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream( return mInput; } -void AudioHardwareGeneric::closeInputStream(AudioStreamInGeneric* in) { - if (in == mInput) mInput = 0; +void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) { + if (mInput && in == mInput) { + delete mInput; + mInput = 0; + } } status_t AudioHardwareGeneric::setVoiceVolume(float v) @@ -185,30 +190,42 @@ status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args) status_t AudioStreamOutGeneric::set( AudioHardwareGeneric *hw, int fd, - int format, - int channels, - uint32_t rate) + 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 (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) + 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() { - if (mAudioHardware) - mAudioHardware->closeOutputStream(this); } ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) @@ -234,10 +251,12 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + 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); @@ -246,29 +265,68 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) 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(); +} + // ---------------------------------------------------------------------------- // record functions status_t AudioStreamInGeneric::set( AudioHardwareGeneric *hw, int fd, - int format, - int channels, - uint32_t rate, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) { // FIXME: remove logging - LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, format, channels, rate); + if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE; + LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) { + 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; } @@ -276,14 +334,12 @@ AudioStreamInGeneric::~AudioStreamInGeneric() { // FIXME: remove logging LOGD("AudioStreamInGeneric destructor"); - if (mAudioHardware) - mAudioHardware->closeInputStream(this); } ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) { // FIXME: remove logging - LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, bytes, mFd); + LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, (int)bytes, mFd); AutoMutex lock(mLock); if (mFd < 0) { LOGE("Attempt to read from unopened device"); @@ -303,10 +359,12 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + 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); @@ -315,6 +373,39 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) 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/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h index c89df87..42da413 100644 --- a/libs/audioflinger/AudioHardwareGeneric.h +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -39,24 +39,28 @@ public: virtual status_t set( AudioHardwareGeneric *hw, int mFd, - int format, - int channelCount, - uint32_t sampleRate); + 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 int channelCount() const { return 2; } + 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 volume) { return INVALID_OPERATION; } + 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); private: AudioHardwareGeneric *mAudioHardware; Mutex mLock; int mFd; + uint32_t mDevice; }; class AudioStreamInGeneric : public AudioStreamIn { @@ -67,24 +71,28 @@ public: virtual status_t set( AudioHardwareGeneric *hw, int mFd, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics); - uint32_t sampleRate() const { return 8000; } + virtual uint32_t sampleRate() const { return 8000; } virtual size_t bufferSize() const { return 320; } - virtual int channelCount() const { return 1; } + 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); private: AudioHardwareGeneric *mAudioHardware; Mutex mLock; int mFd; + uint32_t mDevice; }; @@ -101,28 +109,27 @@ public: virtual status_t setMicMute(bool state); virtual status_t getMicMute(bool* state); - virtual status_t setParameter(const char* key, const char* value) - { return NO_ERROR; } - // create I/O streams virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + 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( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + 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 doRouting() { return NO_ERROR; } virtual status_t dump(int fd, const Vector<String16>& args); private: diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp index cc1bd8f..9a4a7f9 100644 --- a/libs/audioflinger/AudioHardwareInterface.cpp +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -18,6 +18,7 @@ #include <cutils/properties.h> #include <string.h> #include <unistd.h> +//#define LOG_NDEBUG 0 #define LOG_TAG "AudioHardwareInterface" #include <utils/Log.h> @@ -25,15 +26,17 @@ #include "AudioHardwareStub.h" #include "AudioHardwareGeneric.h" +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif -//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file -#ifdef DUMP_FLINGER_OUT +#ifdef ENABLE_AUDIO_DUMP #include "AudioDumpInterface.h" #endif // change to 1 to log routing calls -#define LOG_ROUTING_CALLS 0 +#define LOG_ROUTING_CALLS 1 namespace android { @@ -48,14 +51,6 @@ static const char* routingModeStrings[] = "IN_CALL" }; -static const char* routeStrings[] = -{ - "EARPIECE ", - "SPEAKER ", - "BLUETOOTH ", - "HEADSET ", - "BLUETOOTH_A2DP " -}; static const char* routeNone = "NONE"; static const char* displayMode(int mode) @@ -64,22 +59,6 @@ static const char* displayMode(int mode) return routingModeStrings[0]; return routingModeStrings[mode+3]; } - -static const char* displayRoutes(uint32_t routes) -{ - static char routeStr[80]; - if (routes == 0) - return routeNone; - routeStr[0] = 0; - int bitMask = 1; - for (int i = 0; i < 4; ++i, bitMask <<= 1) { - if (routes & bitMask) { - strcat(routeStr, routeStrings[i]); - } - } - routeStr[strlen(routeStr)-1] = 0; - return routeStr; -} #endif // ---------------------------------------------------------------------------- @@ -112,13 +91,17 @@ AudioHardwareInterface* AudioHardwareInterface::create() hw = new AudioHardwareStub(); } -#ifdef DUMP_FLINGER_OUT +#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 DUMP_FLINGER_OUT. - // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file. - + // 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; @@ -132,48 +115,9 @@ AudioStreamIn::~AudioStreamIn() {} AudioHardwareBase::AudioHardwareBase() { - // force a routing update on initialization - memset(&mRoutes, 0, sizeof(mRoutes)); mMode = 0; } -// generics for audio routing - the real work is done in doRouting -status_t AudioHardwareBase::setRouting(int mode, uint32_t routes) -{ -#if LOG_ROUTING_CALLS - LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes)); -#endif - if (mode == AudioSystem::MODE_CURRENT) - mode = mMode; - if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) - return BAD_VALUE; - uint32_t old = mRoutes[mode]; - mRoutes[mode] = routes; - if ((mode != mMode) || (old == routes)) - return NO_ERROR; -#if LOG_ROUTING_CALLS - const char* oldRouteStr = strdup(displayRoutes(old)); - LOGD("doRouting: mode=%s, old route=[%s], new route=[%s]", - displayMode(mode), oldRouteStr, displayRoutes(routes)); - delete oldRouteStr; -#endif - return doRouting(); -} - -status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes) -{ - if (mode == AudioSystem::MODE_CURRENT) - mode = mMode; - if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) - return BAD_VALUE; - *routes = mRoutes[mode]; -#if LOG_ROUTING_CALLS - LOGD("getRouting: mode=%s, routes=[%s]", - displayMode(mode), displayRoutes(*routes)); -#endif - return NO_ERROR; -} - status_t AudioHardwareBase::setMode(int mode) { #if LOG_ROUTING_CALLS @@ -182,29 +126,24 @@ status_t AudioHardwareBase::setMode(int mode) if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) return BAD_VALUE; if (mMode == mode) - return NO_ERROR; -#if LOG_ROUTING_CALLS - LOGD("doRouting: old mode=%s, new mode=%s route=[%s]", - displayMode(mMode), displayMode(mode), displayRoutes(mRoutes[mode])); -#endif + return ALREADY_EXISTS; mMode = mode; - return doRouting(); + return NO_ERROR; } -status_t AudioHardwareBase::getMode(int* mode) +// default implementation +status_t AudioHardwareBase::setParameters(const String8& keyValuePairs) { - // Implement: set audio routing - *mode = mMode; return NO_ERROR; } -status_t AudioHardwareBase::setParameter(const char* key, const char* value) +// default implementation +String8 AudioHardwareBase::getParameters(const String8& keys) { - // default implementation is to ignore - return NO_ERROR; + AudioParameter param = AudioParameter(keys); + return param.toString(); } - // default implementation size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { @@ -233,10 +172,6 @@ status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); result.append(buffer); - for (int i = 0, n = AudioSystem::NUM_MODES; i < n; ++i) { - snprintf(buffer, SIZE, "\tmRoutes[%d]: %d\n", i, mRoutes[i]); - result.append(buffer); - } ::write(fd, result.string(), result.size()); dump(fd, args); // Dump the state of the concrete child. return NO_ERROR; diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp index 0ab4c60..ae391ee 100644 --- a/libs/audioflinger/AudioHardwareStub.cpp +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -43,10 +43,10 @@ status_t AudioHardwareStub::initCheck() } AudioStreamOut* AudioHardwareStub::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { AudioStreamOutStub* out = new AudioStreamOutStub(); - status_t lStatus = out->set(format, channelCount, sampleRate); + status_t lStatus = out->set(format, channels, sampleRate); if (status) { *status = lStatus; } @@ -56,18 +56,22 @@ AudioStreamOut* AudioHardwareStub::openOutputStream( return 0; } +void AudioHardwareStub::closeOutputStream(AudioStreamOut* out) +{ + delete out; +} + AudioStreamIn* AudioHardwareStub::openInputStream( - int inputSource, int format, int channelCount, uint32_t sampleRate, + 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 ((inputSource < AudioRecord::DEFAULT_INPUT) || - (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { return 0; } AudioStreamInStub* in = new AudioStreamInStub(); - status_t lStatus = in->set(format, channelCount, sampleRate, acoustics); + status_t lStatus = in->set(format, channels, sampleRate, acoustics); if (status) { *status = lStatus; } @@ -77,6 +81,11 @@ AudioStreamIn* AudioHardwareStub::openInputStream( return 0; } +void AudioHardwareStub::closeInputStream(AudioStreamIn* in) +{ + delete in; +} + status_t AudioHardwareStub::setVoiceVolume(float volume) { return NO_ERROR; @@ -107,24 +116,19 @@ status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args) // ---------------------------------------------------------------------------- -status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate) +status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate) { - // fix up defaults - if (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); - if ((format == AudioSystem::PCM_16_BIT) && - (channels == channelCount()) && - (rate == sampleRate())) - return NO_ERROR; - return BAD_VALUE; + return NO_ERROR; } ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) { // fake timing for audio output - usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); return bytes; } @@ -141,29 +145,31 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) 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, "\tchannel count: %d\n", channelCount()); + 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 AudioStreamInStub::set(int format, int channels, uint32_t rate, +status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) { - if ((format == AudioSystem::PCM_16_BIT) && - (channels == channelCount()) && - (rate == sampleRate())) - return NO_ERROR; - return BAD_VALUE; + return NO_ERROR; } ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) { // fake timing for audio input - usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); memset(buffer, 0, bytes); return bytes; } @@ -179,7 +185,7 @@ status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); result.append(buffer); snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); @@ -187,6 +193,12 @@ status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) return NO_ERROR; } +String8 AudioStreamInStub::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + return param.toString(); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h index bf63cc5..583f852 100644 --- a/libs/audioflinger/AudioHardwareStub.h +++ b/libs/audioflinger/AudioHardwareStub.h @@ -29,29 +29,33 @@ namespace android { class AudioStreamOutStub : public AudioStreamOut { public: - virtual status_t set(int format, int channelCount, uint32_t sampleRate); + 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 int channelCount() const { return 2; } + 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 volume) { return NO_ERROR; } + 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); }; class AudioStreamInStub : public AudioStreamIn { public: - virtual status_t set(int format, int channelCount, uint32_t sampleRate, AudioSystem::audio_in_acoustics acoustics); + 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 int channelCount() const { return 1; } + 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); }; class AudioHardwareStub : public AudioHardwareBase @@ -67,26 +71,25 @@ public: virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } - virtual status_t setParameter(const char* key, const char* value) - { return NO_ERROR; } - // create I/O streams virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + 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( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, status_t *status, - AudioSystem::audio_in_acoustics acoustics); + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); protected: - virtual status_t doRouting() { return NO_ERROR; } virtual status_t dump(int fd, const Vector<String16>& args); bool mMicMute; diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index b02efcc..19a442a 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -610,7 +610,6 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, t->in = in; } -inline void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) { for (size_t i=0 ; i<c ; i++) { diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 72ca28a..15766cd 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -85,6 +85,8 @@ public: uint32_t trackNames() const { return mTrackNames; } + static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + private: enum { @@ -176,7 +178,6 @@ private: 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 ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); static void process__validate(state_t* state, void* output); static void process__nop(state_t* state, void* output); diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.cpp b/libs/audioflinger/AudioPolicyManagerGeneric.cpp new file mode 100644 index 0000000..8cfc204 --- /dev/null +++ b/libs/audioflinger/AudioPolicyManagerGeneric.cpp @@ -0,0 +1,945 @@ +/* + * 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 "AudioPolicyManagerGeneric" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include "AudioPolicyManagerGeneric.h" +#include <media/mediarecorder.h> + +namespace android { + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManagerGeneric::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)) { + 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; + 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; + break; + + default: + LOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + 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; + } + return NO_ERROR; + } + + LOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +AudioSystem::device_connection_state AudioPolicyManagerGeneric::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) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } else if (AudioSystem::isInputDevice(device)) { + if (device & mAvailableInputDevices) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } + + return state; +} + +void AudioPolicyManagerGeneric::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; + } + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + + // if leaving or entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (state == AudioSystem::MODE_IN_CALL || + oldState == AudioSystem::MODE_IN_CALL) { + bool starting = (state == AudioSystem::MODE_IN_CALL) ? true : false; + LOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, starting); + } + } +} + +void AudioPolicyManagerGeneric::setRingerMode(uint32_t mode, uint32_t mask) +{ + LOGV("setRingerMode() mode %x, mask %x", mode, mask); + + mRingerMode = mode; +} + +void AudioPolicyManagerGeneric::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + LOGV("setForceUse) usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + mForceUse[usage] = config; +} + +AudioSystem::forced_config AudioPolicyManagerGeneric::getForceUse(AudioSystem::force_use usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManagerGeneric::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 AudioPolicyManagerGeneric::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags) +{ + 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()); + mOutputs.add(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format != 0 && !AudioSystem::isLinearPCM(format)) || + (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && channels != AudioSystem::CHANNEL_OUT_STEREO)) { + return 0; + } + + return mHardwareOutput; +} + +status_t AudioPolicyManagerGeneric::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); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, true); + } + + // incremenent usage count for this stream on the requested output: + outputDesc->changeRefCount(stream, 1); + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::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); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, false); + } + + if (outputDesc->isUsedByStream(stream)) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + return NO_ERROR; + } else { + LOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManagerGeneric::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; + } + } +#endif //AUDIO_POLICY_TEST +} + +audio_io_handle_t AudioPolicyManagerGeneric::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; + + LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics); + + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(); + inputDesc->mDevice = AudioSystem::DEVICE_IN_BUILTIN_MIC; + 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 ((samplingRate != inputDesc->mSamplingRate) || + (format != inputDesc->mFormat) || + (channels != inputDesc->mChannels)) { + LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d", + samplingRate, format, channels); + mpClientInterface->closeInput(input); + delete inputDesc; + return 0; + } + mInputs.add(input, inputDesc); + return input; +} + +status_t AudioPolicyManagerGeneric::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 + for (size_t i = 0; i < mInputs.size(); i++) { + if (mInputs.valueAt(i)->mRefCount > 0) { + LOGW("startInput() input %d, other input %d already started", input, mInputs.keyAt(i)); + return INVALID_OPERATION; + } + } + } + + inputDesc->mRefCount = 1; + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::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 { + inputDesc->mRefCount = 0; + return NO_ERROR; + } +} + +void AudioPolicyManagerGeneric::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); +} + + + +void AudioPolicyManagerGeneric::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManagerGeneric::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + + LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index); + mStreams[stream].mIndexCur = index; + + // do not change actual stream volume if the stream is muted + if (mStreams[stream].mMuteCount != 0) { + return NO_ERROR; + } + + // Do not changed 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("setStreamVolumeIndex() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + // compute and apply stream volume on all outputs according to connected device + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + uint32_t device = outputDesc->device(); + + float volume = computeVolume((int)stream, index, device); + + LOGV("setStreamVolume() for output %d stream %d, volume %f", mOutputs.keyAt(i), stream, volume); + mpClientInterface->setStreamVolume(stream, volume, mOutputs.keyAt(i)); + } + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::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 AudioPolicyManagerGeneric::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); + 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); + 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 Mute Count 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; +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerGeneric +// ---------------------------------------------------------------------------- + +// --- class factory + +AudioPolicyManagerGeneric::AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface) + : +#ifdef AUDIO_POLICY_TEST + Thread(false), +#endif //AUDIO_POLICY_TEST + mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0) +{ + 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_SPEAKER; + mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; + + // 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 { + mOutputs.add(mHardwareOutput, outputDesc); + } + +#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 +} + +AudioPolicyManagerGeneric::~AudioPolicyManagerGeneric() +{ +#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 AudioPolicyManagerGeneric::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()); + mOutputs.add(mHardwareOutput, outputDesc); + } + } + + + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManagerGeneric::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManagerGeneric::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 + +// --- + +AudioPolicyManagerGeneric::routing_strategy AudioPolicyManagerGeneric::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; + } +} + + +float AudioPolicyManagerGeneric::computeVolume(int stream, int index, uint32_t device) +{ + float volume = 1.0; + + StreamDescriptor &streamDesc = mStreams[stream]; + + // Force max volume if stream cannot be muted + if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax; + + int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); + volume = AudioSystem::linearToLog(volInt); + + return volume; +} + +void AudioPolicyManagerGeneric::setStreamMute(int stream, bool on, audio_io_handle_t output) +{ + LOGV("setStreamMute() stream %d, mute %d, output %d", stream, on, output); + + StreamDescriptor &streamDesc = mStreams[stream]; + + if (on) { + if (streamDesc.mMuteCount++ == 0) { + if (streamDesc.mCanBeMuted) { + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, 0, output); + } + } + } else { + if (streamDesc.mMuteCount == 0) { + LOGW("setStreamMute() unmuting non muted stream!"); + return; + } + if (--streamDesc.mMuteCount == 0) { + uint32_t device = mOutputs.valueFor(output)->mDevice; + float volume = computeVolume(stream, streamDesc.mIndexCur, device); + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output); + } + } +} + +void AudioPolicyManagerGeneric::handleIncallSonification(int stream, bool starting) +{ + // 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 (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput); + LOGV("handleIncallSonification() stream %d starting %d device %x", stream, starting, outputDesc->mDevice); + if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) { + if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { + LOGV("handleIncallSonification() low visibility"); + setStreamMute(stream, starting, mHardwareOutput); + } else { + if (starting) { + mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManagerGeneric::AudioOutputDescriptor::AudioOutputDescriptor() + : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0), + mFlags((AudioSystem::output_flags)0), mDevice(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + mRefCount[i] = 0; + } +} + +uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::device() +{ + return mDevice; +} + +void AudioPolicyManagerGeneric::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int 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 AudioPolicyManagerGeneric::AudioOutputDescriptor::refCount() +{ + uint32_t refcount = 0; + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + refcount += mRefCount[i]; + } + return refcount; +} + +status_t AudioPolicyManagerGeneric::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", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Stream refCount\n"); + result.append(buffer); + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + snprintf(buffer, SIZE, " %02d %d\n", i, mRefCount[i]); + result.append(buffer); + } + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManagerGeneric::AudioInputDescriptor::AudioInputDescriptor() + : mSamplingRate(0), mFormat(0), mChannels(0), + mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0) +{ +} + +status_t AudioPolicyManagerGeneric::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 AudioPolicyManagerGeneric::StreamDescriptor::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %02d %02d %02d %02d %d\n", + mIndexMin, + mIndexMax, + mIndexCur, + mMuteCount, + mCanBeMuted); +} + +}; // namespace android diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.h b/libs/audioflinger/AudioPolicyManagerGeneric.h new file mode 100644 index 0000000..4997cdf --- /dev/null +++ b/libs/audioflinger/AudioPolicyManagerGeneric.h @@ -0,0 +1,196 @@ +/* + * 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 <sys/types.h> + +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include <utils/threads.h> + + +namespace android { + +// ---------------------------------------------------------------------------- + +#define MAX_DEVICE_ADDRESS_LEN 20 +#define NUM_TEST_OUTPUTS 5 + +class AudioPolicyManagerGeneric: public AudioPolicyInterface +#ifdef AUDIO_POLICY_TEST + , public Thread +#endif //AUDIO_POLICY_TEST +{ + +public: + AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface); + virtual ~AudioPolicyManagerGeneric(); + + // AudioPolicyInterface + 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 void setPhoneState(int state); + virtual void setRingerMode(uint32_t mode, uint32_t mask); + virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); + virtual void setSystemProperty(const char* property, const char* value); + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags); + 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, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics); + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input); + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual void 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 dump(int fd); + +private: + + enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_DTMF, + NUM_STRATEGIES + }; + + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output + // and keep track of the usage of this output by each audio stream type. + class AudioOutputDescriptor + { + public: + AudioOutputDescriptor(); + + status_t dump(int fd); + + uint32_t device(); + void changeRefCount(AudioSystem::stream_type, int delta); + bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; } + uint32_t refCount(); + + uint32_t mSamplingRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mLatency; // + AudioSystem::output_flags mFlags; // + uint32_t mDevice; // current device this output is routed to + uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output + }; + + // descriptor for audio inputs. Used to maintain current configuration of each opened audio input + // and keep track of the usage of this input. + class AudioInputDescriptor + { + public: + AudioInputDescriptor(); + + status_t dump(int fd); + + uint32_t mSamplingRate; // + uint32_t mFormat; // input configuration + uint32_t mChannels; // + AudioSystem::audio_in_acoustics mAcoustics; // + uint32_t mDevice; // current device this input is routed to + uint32_t mRefCount; // number of AudioRecord clients using this output + }; + + // stream descriptor used for volume control + class StreamDescriptor + { + public: + StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {} + + void dump(char* buffer, size_t size); + + int mIndexMin; // min volume index + int mIndexMax; // max volume index + int mIndexCur; // current volume index + int mMuteCount; // mute request counter + bool mCanBeMuted; // true is the stream can be muted + }; + + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(AudioSystem::stream_type stream); + // return the output handle of an output routed to the specified device, 0 if no output + // is routed to the device + float computeVolume(int stream, int index, uint32_t device); + // Mute or unmute the stream on the specified output + void setStreamMute(int stream, bool on, audio_io_handle_t output); + // handle special cases for sonification strategy while in call: mute streams or replace by + // a special tone in the device used for communication + void handleIncallSonification(int stream, bool starting); + + +#ifdef AUDIO_POLICY_TEST + virtual bool threadLoop(); + void exit(); + int testOutputIndex(audio_io_handle_t output); +#endif //AUDIO_POLICY_TEST + + + AudioPolicyClientInterface *mpClientInterface; // audio policy client interface + audio_io_handle_t mHardwareOutput; // hardware output handler + + KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; // list ot output descritors + KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors + uint32_t mAvailableOutputDevices; // bit field of all available output devices + uint32_t mAvailableInputDevices; // bit field of all available input devices + int mPhoneState; // current phone state + uint32_t mRingerMode; // current ringer mode + AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration + + StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control + +#ifdef AUDIO_POLICY_TEST + Mutex mLock; + Condition mWaitWorkCV; + + int mCurOutput; + bool mDirectOutput; + audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; + int mTestInput; + uint32_t mTestDevice; + uint32_t mTestSamplingRate; + uint32_t mTestFormat; + uint32_t mTestChannels; + uint32_t mTestLatencyMs; +#endif //AUDIO_POLICY_TEST + +}; + +}; diff --git a/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp new file mode 100644 index 0000000..aa48019 --- /dev/null +++ b/libs/audioflinger/AudioPolicyService.cpp @@ -0,0 +1,911 @@ +/* + * 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 "AudioPolicyManagerGeneric.h" +#include <cutils/properties.h> +#include <dlfcn.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(); + // start audio commands thread + mAudioCommandThread = new AudioCommandThread(); + +#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST) + mpPolicyManager = new AudioPolicyManagerGeneric(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 AudioPolicyManagerGeneric(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() + : Thread(false) +{ + mpToneGenerator = NULL; +} + + +AudioPolicyService::AudioCommandThread::~AudioCommandThread() +{ + mAudioCommands.clear(); + if (mpToneGenerator != NULL) delete mpToneGenerator; +} + +void AudioPolicyService::AudioCommandThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "AudioCommandThread"); + + run(buffer, 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; + } + } + 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); + + // 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 %ld, num commands %d", command->mCommand, 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/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h new file mode 100644 index 0000000..b9234ec --- /dev/null +++ b/libs/audioflinger/AudioPolicyService.h @@ -0,0 +1,222 @@ +/* + * 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 (); + 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; + }; + + // 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/libs/binder/Android.mk b/libs/binder/Android.mk new file mode 100644 index 0000000..2df6775 --- /dev/null +++ b/libs/binder/Android.mk @@ -0,0 +1,45 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + Permission.cpp \ + ProcessState.cpp \ + Static.cpp + +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libutils + +LOCAL_MODULE:= libbinder + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/Binder.cpp b/libs/binder/Binder.cpp index 37e4685..0dd7622 100644 --- a/libs/utils/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include <utils/Binder.h> +#include <binder/Binder.h> #include <utils/Atomic.h> -#include <utils/BpBinder.h> -#include <utils/IInterface.h> -#include <utils/Parcel.h> +#include <binder/BpBinder.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> #include <stdio.h> @@ -27,6 +27,17 @@ namespace android { // --------------------------------------------------------------------------- +IBinder::IBinder() + : RefBase() +{ +} + +IBinder::~IBinder() +{ +} + +// --------------------------------------------------------------------------- + sp<IInterface> IBinder::queryLocalInterface(const String16& descriptor) { return NULL; @@ -58,6 +69,8 @@ public: // --------------------------------------------------------------------------- +String16 BBinder::sEmptyDescriptor; + BBinder::BBinder() : mExtras(NULL) { @@ -73,10 +86,10 @@ status_t BBinder::pingBinder() return NO_ERROR; } -String16 BBinder::getInterfaceDescriptor() const +const String16& BBinder::getInterfaceDescriptor() const { LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return String16(); + return sEmptyDescriptor; } status_t BBinder::transact( diff --git a/libs/utils/BpBinder.cpp b/libs/binder/BpBinder.cpp index 69ab195..5de87ec 100644 --- a/libs/utils/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -17,9 +17,9 @@ #define LOG_TAG "BpBinder" //#define LOG_NDEBUG 0 -#include <utils/BpBinder.h> +#include <binder/BpBinder.h> -#include <utils/IPCThreadState.h> +#include <binder/IPCThreadState.h> #include <utils/Log.h> #include <stdio.h> @@ -98,16 +98,33 @@ BpBinder::BpBinder(int32_t handle) IPCThreadState::self()->incWeakHandle(handle); } -String16 BpBinder::getInterfaceDescriptor() const +bool BpBinder::isDescriptorCached() const { + Mutex::Autolock _l(mLock); + return mDescriptorCache.size() ? true : false; +} + +const String16& BpBinder::getInterfaceDescriptor() const { - String16 res; - Parcel send, reply; - status_t err = const_cast<BpBinder*>(this)->transact( - INTERFACE_TRANSACTION, send, &reply); - if (err == NO_ERROR) { - res = reply.readString16(); + if (isDescriptorCached() == false) { + Parcel send, reply; + // do the IPC without a lock held. + status_t err = const_cast<BpBinder*>(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + String16 res(reply.readString16()); + Mutex::Autolock _l(mLock); + // mDescriptorCache could have been assigned while the lock was + // released. + if (mDescriptorCache.size() == 0) + mDescriptorCache = res; + } } - return res; + + // we're returning a reference to a non-static object here. Usually this + // is not something smart to do, however, with binder objects it is + // (usually) safe because they are reference-counted. + + return mDescriptorCache; } bool BpBinder::isBinderAlive() const diff --git a/libs/utils/IInterface.cpp b/libs/binder/IInterface.cpp index 6ea8178..29acf5d 100644 --- a/libs/utils/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -14,12 +14,19 @@ * limitations under the License. */ -#include <utils/IInterface.h> +#include <binder/IInterface.h> namespace android { // --------------------------------------------------------------------------- +IInterface::IInterface() + : RefBase() { +} + +IInterface::~IInterface() { +} + sp<IBinder> IInterface::asBinder() { return this ? onAsBinder() : NULL; diff --git a/libs/utils/IMemory.cpp b/libs/binder/IMemory.cpp index 429bc2b..6c1d225 100644 --- a/libs/utils/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -25,11 +25,11 @@ #include <sys/types.h> #include <sys/mman.h> -#include <utils/IMemory.h> +#include <binder/IMemory.h> #include <utils/KeyedVector.h> #include <utils/threads.h> #include <utils/Atomic.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <utils/CallStack.h> #define VERBOSE 0 @@ -205,11 +205,11 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) +BnMemory::BnMemory() { +} + +BnMemory::~BnMemory() { +} status_t BnMemory::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) @@ -299,11 +299,11 @@ void BpMemoryHeap::assertReallyMapped() const ssize_t size = reply.readInt32(); uint32_t flags = reply.readInt32(); - LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)", asBinder().get(), parcel_fd, size, err, strerror(-err)); int fd = dup( parcel_fd ); - LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; @@ -316,7 +316,7 @@ void BpMemoryHeap::assertReallyMapped() const mRealHeap = true; mBase = mmap(0, size, access, MAP_SHARED, fd, 0); if (mBase == MAP_FAILED) { - LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", asBinder().get(), size, fd, strerror(errno)); close(fd); } else { @@ -357,8 +357,14 @@ uint32_t BpMemoryHeap::getFlags() const { IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); +BnMemoryHeap::BnMemoryHeap() { +} + +BnMemoryHeap::~BnMemoryHeap() { +} + status_t BnMemoryHeap::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case HEAP_ID: { diff --git a/libs/utils/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 04ae142..b2a7db8 100644 --- a/libs/utils/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -14,17 +14,20 @@ * limitations under the License. */ -#include <utils/IPCThreadState.h> +#define LOG_TAG "IPCThreadState" -#include <utils/Binder.h> -#include <utils/BpBinder.h> +#include <binder/IPCThreadState.h> + +#include <binder/Binder.h> +#include <binder/BpBinder.h> +#include <cutils/sched_policy.h> #include <utils/Debug.h> #include <utils/Log.h> #include <utils/TextOutput.h> #include <utils/threads.h> -#include <private/utils/binder_module.h> -#include <private/utils/Static.h> +#include <private/binder/binder_module.h> +#include <private/binder/Static.h> #include <sys/ioctl.h> #include <signal.h> @@ -366,13 +369,8 @@ void IPCThreadState::restoreCallingIdentity(int64_t token) void IPCThreadState::clearCaller() { - if (mProcess->supportsProcesses()) { - mCallingPid = getpid(); - mCallingUid = getuid(); - } else { - mCallingPid = -1; - mCallingUid = -1; - } + mCallingPid = getpid(); + mCallingUid = getuid(); } void IPCThreadState::flushCommands() @@ -423,9 +421,26 @@ void IPCThreadState::joinThreadPool(bool isMain) alog << "Processing top-level Command: " << getReturnString(cmd) << endl; } + + result = executeCommand(cmd); } + // After executing the command, ensure that the thread is returned to the + // default cgroup and priority before rejoining the pool. This is a failsafe + // in case the command implementation failed to properly restore the thread's + // scheduling parameters upon completion. + int my_id; +#ifdef HAVE_GETTID + my_id = gettid(); +#else + my_id = getpid(); +#endif + if (!set_sched_policy(my_id, SP_FOREGROUND)) { + // success; reset the priority as well + setpriority(PRIO_PROCESS, my_id, ANDROID_PRIORITY_NORMAL); + } + // Let this thread exit the thread pool if it is no longer // needed and it is not the main process thread. if(result == TIMED_OUT && !isMain) { diff --git a/libs/utils/IPermissionController.cpp b/libs/binder/IPermissionController.cpp index f01d38f..bff4c9b 100644 --- a/libs/utils/IPermissionController.cpp +++ b/libs/binder/IPermissionController.cpp @@ -16,14 +16,14 @@ #define LOG_TAG "PermissionController" -#include <utils/IPermissionController.h> +#include <binder/IPermissionController.h> #include <utils/Debug.h> #include <utils/Log.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <utils/String8.h> -#include <private/utils/Static.h> +#include <private/binder/Static.h> namespace android { @@ -55,12 +55,6 @@ IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnPermissionController::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/utils/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 9beeadd..0cf4158 100644 --- a/libs/utils/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -16,16 +16,16 @@ #define LOG_TAG "ServiceManager" -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/Debug.h> -#include <utils/IPCThreadState.h> #include <utils/Log.h> -#include <utils/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/Parcel.h> #include <utils/String8.h> #include <utils/SystemClock.h> -#include <private/utils/Static.h> +#include <private/binder/Static.h> #include <unistd.h> @@ -53,14 +53,19 @@ bool checkCallingPermission(const String16& permission) static String16 _permission("permission"); + bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) { IPCThreadState* ipcState = IPCThreadState::self(); - int32_t pid = ipcState->getCallingPid(); - int32_t uid = ipcState->getCallingUid(); + pid_t pid = ipcState->getCallingPid(); + uid_t uid = ipcState->getCallingUid(); if (outPid) *outPid = pid; - if (outUid) *outUid= uid; - + if (outUid) *outUid = uid; + return checkPermission(permission, pid, uid); +} + +bool checkPermission(const String16& permission, pid_t pid, uid_t uid) +{ sp<IPermissionController> pc; gDefaultServiceManagerLock.lock(); pc = gPermissionController; @@ -178,12 +183,6 @@ IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnServiceManager::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/utils/MemoryBase.cpp b/libs/binder/MemoryBase.cpp index f25e11c..033066b 100644 --- a/libs/utils/MemoryBase.cpp +++ b/libs/binder/MemoryBase.cpp @@ -18,7 +18,7 @@ #include <stdlib.h> #include <stdint.h> -#include <utils/MemoryBase.h> +#include <binder/MemoryBase.h> namespace android { diff --git a/libs/utils/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index cf8201b..d5ffe7f 100644 --- a/libs/utils/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -16,13 +16,13 @@ #define LOG_TAG "MemoryDealer" -#include <utils/MemoryDealer.h> +#include <binder/MemoryDealer.h> #include <utils/Log.h> -#include <utils/IPCThreadState.h> +#include <binder/IPCThreadState.h> #include <utils/SortedVector.h> #include <utils/String8.h> -#include <utils/MemoryBase.h> +#include <binder/MemoryBase.h> #include <stdint.h> #include <stdio.h> @@ -38,7 +38,15 @@ #include <sys/file.h> namespace android { +// ---------------------------------------------------------------------------- +HeapInterface::HeapInterface() { } +HeapInterface::~HeapInterface() { } + +// ---------------------------------------------------------------------------- + +AllocatorInterface::AllocatorInterface() { } +AllocatorInterface::~AllocatorInterface() { } // ---------------------------------------------------------------------------- @@ -107,7 +115,7 @@ sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags) if (new_memory != 0) { memory = new Allocation(this, offset, size, new_memory); } else { - LOGE("couldn't map [%8x, %d]", offset, size); + LOGE("couldn't map [%8lx, %u]", offset, size); if (size) { /* NOTE: it's VERY important to not free allocations of size 0 * because they're special as they don't have any record in the @@ -339,6 +347,10 @@ void SimpleBestFitAllocator::dump_l(String8& result, // ---------------------------------------------------------------------------- +SharedHeap::SharedHeap() + : HeapInterface(), MemoryHeapBase() +{ +} SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) : MemoryHeapBase(size, flags, name) diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 8251728..624f7eb 100644 --- a/libs/utils/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -29,7 +29,7 @@ #include <cutils/ashmem.h> #include <cutils/atomic.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapBase.h> #if HAVE_ANDROID_OS #include <linux/android_pmem.h> @@ -67,7 +67,11 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(0), mNeedUnmap(false) { - int fd = open(device, O_RDWR); + int open_flags = O_RDWR; + if (flags & NO_CACHING) + open_flags |= O_SYNC; + + int fd = open(device, open_flags); LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); if (fd >= 0) { const size_t pagesize = getpagesize(); @@ -78,13 +82,13 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) } } -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(0), mNeedUnmap(false) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); - mapfd(dup(fd), size); + mapfd(dup(fd), size, offset); } status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) @@ -100,7 +104,7 @@ status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const cha return NO_ERROR; } -status_t MemoryHeapBase::mapfd(int fd, size_t size) +status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { if (size == 0) { // try to figure out the size automatically @@ -121,7 +125,7 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size) if ((mFlags & DONT_MAP_LOCALLY) == 0) { void* base = (uint8_t*)mmap(0, size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { LOGE("mmap(fd=%d, size=%u) failed (%s)", fd, uint32_t(size), strerror(errno)); diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp index eba2b30..c660947 100644 --- a/libs/utils/MemoryHeapPmem.cpp +++ b/libs/binder/MemoryHeapPmem.cpp @@ -27,8 +27,8 @@ #include <cutils/log.h> -#include <utils/MemoryHeapPmem.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapPmem.h> +#include <binder/MemoryHeapBase.h> #if HAVE_ANDROID_OS #include <linux/android_pmem.h> @@ -108,7 +108,7 @@ void SubRegionMemory::revoke() // promote() it. #if HAVE_ANDROID_OS - if (mSize != NULL) { + if (mSize != 0) { const sp<MemoryHeapPmem>& heap(getHeap()); int our_fd = heap->heapID(); struct pmem_region sub; @@ -132,7 +132,7 @@ MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, char const * const device = pmemHeap->getDevice(); #if HAVE_ANDROID_OS if (device) { - int fd = open(device, O_RDWR); + int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0)); LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); if (fd >= 0) { int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); diff --git a/libs/utils/Parcel.cpp b/libs/binder/Parcel.cpp index b0e3750..e397bce 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -17,19 +17,19 @@ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 -#include <utils/Parcel.h> +#include <binder/Parcel.h> -#include <utils/Binder.h> -#include <utils/BpBinder.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> #include <utils/Debug.h> -#include <utils/ProcessState.h> +#include <binder/ProcessState.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/misc.h> -#include <private/utils/binder_module.h> +#include <private/binder/binder_module.h> #include <stdio.h> #include <stdlib.h> @@ -441,9 +441,14 @@ status_t Parcel::writeInterfaceToken(const String16& interface) return writeString16(interface); } +bool Parcel::checkInterface(IBinder* binder) const +{ + return enforceInterface(binder->getInterfaceDescriptor()); +} + bool Parcel::enforceInterface(const String16& interface) const { - String16 str = readString16(); + const String16 str(readString16()); if (str == interface) { return true; } else { @@ -557,54 +562,27 @@ restart_write: status_t Parcel::writeInt32(int32_t val) { - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast<int32_t*>(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; + return writeAligned(val); } status_t Parcel::writeInt64(int64_t val) { - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast<int64_t*>(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; + return writeAligned(val); } status_t Parcel::writeFloat(float val) { - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast<float*>(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; + return writeAligned(val); } status_t Parcel::writeDouble(double val) { - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast<double*>(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } + return writeAligned(val); +} - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; +status_t Parcel::writeIntPtr(intptr_t val) +{ + return writeAligned(val); } status_t Parcel::writeCString(const char* str) @@ -656,7 +634,7 @@ status_t Parcel::writeWeakBinder(const wp<IBinder>& val) status_t Parcel::writeNativeHandle(const native_handle* handle) { - if (handle->version != sizeof(native_handle)) + if (!handle || handle->version != sizeof(native_handle)) return BAD_TYPE; status_t err; @@ -763,103 +741,98 @@ const void* Parcel::readInplace(size_t len) const return NULL; } -status_t Parcel::readInt32(int32_t *pArg) const -{ - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { +template<class T> +status_t Parcel::readAligned(T *pArg) const { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T)); + + if ((mDataPos+sizeof(T)) <= mDataSize) { const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - *pArg = *reinterpret_cast<const int32_t*>(data); + mDataPos += sizeof(T); + *pArg = *reinterpret_cast<const T*>(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; } } +template<class T> +T Parcel::readAligned() const { + T result; + if (readAligned(&result) != NO_ERROR) { + result = 0; + } + + return result; +} + +template<class T> +status_t Parcel::writeAligned(T val) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T)); + + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast<T*>(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::readInt32(int32_t *pArg) const +{ + return readAligned(pArg); +} + int32_t Parcel::readInt32() const { - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast<const int32_t*>(data); - } - return 0; + return readAligned<int32_t>(); } status_t Parcel::readInt64(int64_t *pArg) const { - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - *pArg = *reinterpret_cast<const int64_t*>(data); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } + return readAligned(pArg); } int64_t Parcel::readInt64() const { - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast<const int64_t*>(data); - } - return 0; + return readAligned<int64_t>(); } status_t Parcel::readFloat(float *pArg) const { - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast<const float*>(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } + return readAligned(pArg); } float Parcel::readFloat() const { - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast<const float*>(data); - } - return 0; + return readAligned<float>(); } status_t Parcel::readDouble(double *pArg) const { - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast<const double*>(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } + return readAligned(pArg); } double Parcel::readDouble() const { - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast<const double*>(data); - } - return 0; + return readAligned<double>(); +} + +status_t Parcel::readIntPtr(intptr_t *pArg) const +{ + return readAligned(pArg); +} + + +intptr_t Parcel::readIntPtr() const +{ + return readAligned<intptr_t>(); } diff --git a/libs/binder/Permission.cpp b/libs/binder/Permission.cpp new file mode 100644 index 0000000..fd8fe69 --- /dev/null +++ b/libs/binder/Permission.cpp @@ -0,0 +1,88 @@ +/* + * 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 <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Permission.h> + +namespace android { +// --------------------------------------------------------------------------- + +Permission::Permission(char const* name) + : mPermissionName(name), mPid(getpid()) +{ +} + +Permission::Permission(const String16& name) + : mPermissionName(name), mPid(getpid()) +{ +} + +Permission::Permission(const Permission& rhs) + : mPermissionName(rhs.mPermissionName), + mGranted(rhs.mGranted), + mPid(rhs.mPid) +{ +} + +Permission::~Permission() +{ +} + +bool Permission::operator < (const Permission& rhs) const +{ + return mPermissionName < rhs.mPermissionName; +} + +bool Permission::checkCalling() const +{ + IPCThreadState* ipcState = IPCThreadState::self(); + pid_t pid = ipcState->getCallingPid(); + uid_t uid = ipcState->getCallingUid(); + return doCheckPermission(pid, uid); +} + +bool Permission::check(pid_t pid, uid_t uid) const +{ + return doCheckPermission(pid, uid); +} + +bool Permission::doCheckPermission(pid_t pid, uid_t uid) const +{ + if ((uid == 0) || (pid == mPid)) { + // root and ourselves is always okay + return true; + } else { + // see if we already granted this permission for this uid + Mutex::Autolock _l(mLock); + if (mGranted.indexOf(uid) >= 0) + return true; + } + + bool granted = checkPermission(mPermissionName, pid, uid); + if (granted) { + Mutex::Autolock _l(mLock); + // no need to check again, the old item will be replaced if it is + // already there. + mGranted.add(uid); + } + return granted; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/ProcessState.cpp b/libs/binder/ProcessState.cpp index 4567df6..d7daf73 100644 --- a/libs/utils/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -18,19 +18,19 @@ #include <cutils/process_name.h> -#include <utils/ProcessState.h> +#include <binder/ProcessState.h> #include <utils/Atomic.h> -#include <utils/BpBinder.h> -#include <utils/IPCThreadState.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> #include <utils/Log.h> #include <utils/String8.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/String8.h> #include <utils/threads.h> -#include <private/utils/binder_module.h> -#include <private/utils/Static.h> +#include <private/binder/binder_module.h> +#include <private/binder/Static.h> #include <errno.h> #include <fcntl.h> diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp new file mode 100644 index 0000000..12b0308 --- /dev/null +++ b/libs/binder/Static.cpp @@ -0,0 +1,53 @@ +/* + * 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. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include <private/binder/Static.h> + +#include <binder/IPCThreadState.h> +#include <utils/Log.h> + +namespace android { + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp<ProcessState> gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp<IServiceManager> gDefaultServiceManager; +sp<IPermissionController> gPermissionController; + +} // namespace android diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index ec5aa3f..eb51c22 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -5,44 +5,50 @@ LOCAL_SRC_FILES:= \ clz.cpp.arm \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ - GPUHardware/GPUHardware.cpp \ BlurFilter.cpp.arm \ - CPUGauge.cpp \ Layer.cpp \ LayerBase.cpp \ LayerBuffer.cpp \ LayerBlur.cpp \ - LayerBitmap.cpp \ LayerDim.cpp \ - LayerOrientationAnim.cpp \ - OrientationAnimation.cpp \ + MessageQueue.cpp \ SurfaceFlinger.cpp \ Tokenizer.cpp \ - Transform.cpp \ - VRamHeap.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 +ifeq ($(TARGET_BOARD_PLATFORM), qsd8k) + 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 + LOCAL_LDLIBS += -lrt -lpthread endif endif LOCAL_SHARED_LIBRARIES := \ - libhardware \ - libutils \ libcutils \ - libui \ - libcorecg \ - libsgl \ libpixelflinger \ + libhardware \ + libutils \ + libskia \ libEGL \ - libGLESv1_CM + libGLESv1_CM \ + libbinder \ + libui 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/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp index 5dc0ba0..1ffbd5b 100644 --- a/libs/surfaceflinger/BlurFilter.cpp +++ b/libs/surfaceflinger/BlurFilter.cpp @@ -111,6 +111,50 @@ struct BlurColor565 } }; +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; @@ -316,7 +360,13 @@ status_t blurFilter( int kernelSizeUser, int repeat) { - return blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, 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 diff --git a/libs/surfaceflinger/CPUGauge.cpp b/libs/surfaceflinger/CPUGauge.cpp deleted file mode 100644 index 74a9270..0000000 --- a/libs/surfaceflinger/CPUGauge.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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 "CPUGauge" - -#include <stdint.h> -#include <limits.h> -#include <sys/types.h> -#include <math.h> - -#include <utils/threads.h> -#include <utils/Errors.h> -#include <utils/Log.h> - -#include <ui/PixelFormat.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <ui/DisplayInfo.h> -#include <ui/ISurfaceComposer.h> -#include <ui/ISurfaceFlingerClient.h> - -#include <pixelflinger/pixelflinger.h> - -#include "CPUGauge.h" - -namespace android { - -CPUGauge::CPUGauge( const sp<ISurfaceComposer>& composer, - nsecs_t interval, - int clock, - int refclock) - : Thread(false), - mInterval(interval), mClock(clock), mRefClock(refclock), - mReferenceTime(0), - mReferenceWorkingTime(0), mCpuUsage(0), - mRefIdleTime(0), mIdleTime(0) -{ - mFd = fopen("/proc/stat", "r"); - setvbuf(mFd, NULL, _IONBF, 0); - - mSession = SurfaceComposerClient::clientForConnection( - composer->createConnection()->asBinder()); -} - -CPUGauge::~CPUGauge() -{ - fclose(mFd); -} - -const sp<SurfaceComposerClient>& CPUGauge::session() const -{ - return mSession; -} - -void CPUGauge::onFirstRef() -{ - run("CPU Gauge"); -} - -status_t CPUGauge::readyToRun() -{ - LOGI("Starting CPU gauge..."); - return NO_ERROR; -} - -bool CPUGauge::threadLoop() -{ - DisplayInfo dinfo; - session()->getDisplayInfo(0, &dinfo); - sp<Surface> s(session()->createSurface(getpid(), 0, dinfo.w, 4, PIXEL_FORMAT_OPAQUE)); - session()->openTransaction(); - s->setLayer(INT_MAX); - session()->closeTransaction(); - - static const GGLfixed colors[4][4] = { - { 0x00000, 0x10000, 0x00000, 0x10000 }, - { 0x10000, 0x10000, 0x00000, 0x10000 }, - { 0x10000, 0x00000, 0x00000, 0x10000 }, - { 0x00000, 0x00000, 0x00000, 0x10000 }, - }; - - GGLContext* gl; - gglInit(&gl); - gl->activeTexture(gl, 0); - gl->disable(gl, GGL_TEXTURE_2D); - gl->disable(gl, GGL_BLEND); - - const int w = dinfo.w; - - while(!exitPending()) - { - mLock.lock(); - const float cpuUsage = this->cpuUsage(); - const float totalCpuUsage = 1.0f - idle(); - mLock.unlock(); - - Surface::SurfaceInfo info; - s->lock(&info); - GGLSurface fb; - fb.version = sizeof(GGLSurface); - fb.width = info.w; - fb.height = info.h; - fb.stride = info.w; - fb.format = info.format; - fb.data = (GGLubyte*)info.bits; - - gl->colorBuffer(gl, &fb); - gl->color4xv(gl, colors[3]); - gl->recti(gl, 0, 0, w, 4); - gl->color4xv(gl, colors[2]); // red - gl->recti(gl, 0, 0, int(totalCpuUsage*w), 2); - gl->color4xv(gl, colors[0]); // green - gl->recti(gl, 0, 2, int(cpuUsage*w), 4); - - s->unlockAndPost(); - - usleep(ns2us(mInterval)); - } - - gglUninit(gl); - return false; -} - -void CPUGauge::sample() -{ - if (mLock.tryLock() == NO_ERROR) { - const nsecs_t now = systemTime(mRefClock); - const nsecs_t referenceTime = now-mReferenceTime; - if (referenceTime >= mInterval) { - const float reftime = 1.0f / referenceTime; - const nsecs_t nowWorkingTime = systemTime(mClock); - - char buf[256]; - fgets(buf, 256, mFd); - rewind(mFd); - char *str = buf+5; - char const * const usermode = strsep(&str, " "); (void)usermode; - char const * const usernice = strsep(&str, " "); (void)usernice; - char const * const systemmode = strsep(&str, " ");(void)systemmode; - char const * const idle = strsep(&str, " "); - const nsecs_t nowIdleTime = atoi(idle) * 10000000LL; - mIdleTime = float(nowIdleTime - mRefIdleTime) * reftime; - mRefIdleTime = nowIdleTime; - - const nsecs_t workingTime = nowWorkingTime - mReferenceWorkingTime; - const float newCpuUsage = float(workingTime) * reftime; - if (mCpuUsage != newCpuUsage) { - mCpuUsage = newCpuUsage; - mReferenceWorkingTime = nowWorkingTime; - mReferenceTime = now; - } - } - mLock.unlock(); - } -} - - -}; // namespace android diff --git a/libs/surfaceflinger/CPUGauge.h b/libs/surfaceflinger/CPUGauge.h deleted file mode 100644 index 5bb53c0..0000000 --- a/libs/surfaceflinger/CPUGauge.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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_CPUGAUGE_H -#define ANDROID_CPUGAUGE_H - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Timers.h> - -#include <ui/SurfaceComposerClient.h> - -namespace android { - -class CPUGauge : public Thread -{ -public: - CPUGauge( const sp<ISurfaceComposer>& composer, - nsecs_t interval=s2ns(1), - int clock=SYSTEM_TIME_THREAD, - int refclock=SYSTEM_TIME_MONOTONIC); - - ~CPUGauge(); - - const sp<SurfaceComposerClient>& session() const; - - void sample(); - - inline float cpuUsage() const { return mCpuUsage; } - inline float idle() const { return mIdleTime; } - -private: - virtual void onFirstRef(); - virtual status_t readyToRun(); - virtual bool threadLoop(); - - Mutex mLock; - - sp<SurfaceComposerClient> mSession; - - const nsecs_t mInterval; - const int mClock; - const int mRefClock; - - nsecs_t mReferenceTime; - nsecs_t mReferenceWorkingTime; - float mCpuUsage; - nsecs_t mRefIdleTime; - float mIdleTime; - FILE* mFd; -}; - - -}; // namespace android - -#endif // ANDROID_CPUGAUGE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index ab02fa0..1abfd68 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -23,63 +21,50 @@ #include <cutils/properties.h> +#include <utils/RefBase.h> #include <utils/Log.h> -#include <ui/EGLDisplaySurface.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)) -const char *egl_strerror(EGLint err) -{ - switch (err){ - case EGL_SUCCESS: return "EGL_SUCCESS"; - case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; - case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; - case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; - case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; - case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; - case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; - case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; - case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; - case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; - case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; - case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; - case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; - case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; - case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; - default: return "UNKNOWN"; - } -} static __attribute__((noinline)) void checkGLErrors() { - GLenum error = glGetError(); - if (error != GL_NO_ERROR) + 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(); - // GLESonGL seems to be returning 0 when there is no errors? - if (error && error != EGL_SUCCESS) - LOGE("%s error 0x%04x (%s)", - token, int(error), egl_strerror(error)); + 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. * @@ -108,20 +93,37 @@ PixelFormat DisplayHardware::getFormat() const { return mFormat; } 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 - const EGLint attribs[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 0, + 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, n; - EGLConfig config; + EGLint numConfigs=0; EGLSurface surface; EGLContext context; - mFlags = 0; + mFlags = CACHED_BUFFERS; // TODO: all the extensions below should be queried through // eglGetProcAddress(). @@ -129,7 +131,17 @@ void DisplayHardware::init(uint32_t dpy) EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, NULL, NULL); eglGetConfigs(display, NULL, 0, &numConfigs); - eglChooseConfig(display, attribs, &config, 1, &n); + + 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 @@ -144,13 +156,13 @@ void DisplayHardware::init(uint32_t dpy) LOGI("version : %s", eglQueryString(display, EGL_VERSION)); LOGI("extensions: %s", egl_extensions); LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - - // TODO: get this from the devfb driver (probably should be HAL module) - mFlags |= SWAP_RECTANGLE_EXTENSION; + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); - // TODO: get the real "update_on_demand" behavior (probably should be HAL module) - mFlags |= UPDATE_ON_DEMAND; + if (mNativeWindow->isUpdateOnDemand()) { + mFlags |= PARTIAL_UPDATES; + } + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { if (dummy == EGL_SLOW_CONFIG) mFlags |= SLOW_CONFIG; @@ -160,37 +172,48 @@ void DisplayHardware::init(uint32_t dpy) * Create our main surface */ - mDisplaySurface = new EGLDisplaySurface(); + surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); - surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL); - //checkEGLErrors("eglCreateDisplaySurfaceANDROID"); + 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; } } - - GLint value = EGL_UNKNOWN; - eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value); - if (value == EGL_UNKNOWN) { - mDpiX = 160.0f; - } else { - mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING; - } - value = EGL_UNKNOWN; - eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value); - if (value == EGL_UNKNOWN) { - mDpiY = 160.0f; - } else { - mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING; + + 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; + } } - mRefreshRate = 60.f; // TODO: get the real refresh rate + // 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; - char property[PROPERTY_VALUE_MAX]; /* Read density from build-specific ro.sf.lcd_density property - * except if it is overriden by qemu.sf.lcd_density. + * 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) { @@ -209,11 +232,6 @@ void DisplayHardware::init(uint32_t dpy) */ context = eglCreateContext(display, config, NULL, NULL); - //checkEGLErrors("eglCreateContext"); - - eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); - eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); - /* * Gather OpenGL ES extensions @@ -221,21 +239,33 @@ void DisplayHardware::init(uint32_t dpy) 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", glGetString(GL_RENDERER)); + LOGI("renderer : %s", gl_renderer); LOGI("version : %s", glGetString(GL_VERSION)); LOGI("extensions: %s", gl_extensions); + if (strstr(gl_renderer, "PowerVR SGX 530")) { + LOGD("Assuming uncached graphics buffers."); + mFlags &= ~CACHED_BUFFERS; + } 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; } - if (strstr(gl_extensions, "GL_ANDROID_direct_texture")) { +#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); @@ -244,19 +274,8 @@ void DisplayHardware::init(uint32_t dpy) mConfig = config; mSurface = surface; mContext = context; - mFormat = GGL_PIXEL_FORMAT_RGB_565; - - hw_module_t const* module; - - mBlitEngine = NULL; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - copybit_open(module, &mBlitEngine); - } - - mOverlayEngine = NULL; - if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { - overlay_control_open(module, &mOverlayEngine); - } + mFormat = fbDev->format; + mPageFlipCount = 0; } /* @@ -270,7 +289,6 @@ void DisplayHardware::fini() { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mDisplay); - copybit_close(mBlitEngine); overlay_control_close(mOverlayEngine); } @@ -284,33 +302,13 @@ void DisplayHardware::acquireScreen() const DisplayHardwareBase::acquireScreen(); } -void DisplayHardware::getDisplaySurface(copybit_image_t* img) const -{ - img->w = mDisplaySurface->stride; - img->h = mDisplaySurface->height; - img->format = mDisplaySurface->format; - img->offset = mDisplaySurface->offset; - img->base = (void*)mDisplaySurface->base; - img->fd = mDisplaySurface->fd; -} - -void DisplayHardware::getDisplaySurface(GGLSurface* fb) const -{ - fb->version= sizeof(GGLSurface); - fb->width = mDisplaySurface->width; - fb->height = mDisplaySurface->height; - fb->stride = mDisplaySurface->stride; - fb->format = mDisplaySurface->format; - fb->data = (GGLubyte*)mDisplaySurface->base + mDisplaySurface->offset; -} - uint32_t DisplayHardware::getPageFlipCount() const { - return mDisplaySurface->getPageFlipCount(); + return mPageFlipCount; } -/* - * "Flip" the front and back buffers. - */ +status_t DisplayHardware::compositionComplete() const { + return mNativeWindow->compositionComplete(); +} void DisplayHardware::flip(const Region& dirty) const { @@ -319,21 +317,20 @@ void DisplayHardware::flip(const Region& dirty) const EGLDisplay dpy = mDisplay; EGLSurface surface = mSurface; - Region newDirty(dirty); - newDirty.andSelf(Rect(mWidth, mHeight)); - - if (mFlags & BUFFER_PRESERVED) { - const Region copyback(mDirty.subtract(newDirty)); - mDirty = newDirty; - mDisplaySurface->copyFrontToBack(copyback); - } - - if (mFlags & SWAP_RECTANGLE_EXTENSION) { - const Rect& b(newDirty.bounds()); - mDisplaySurface->setSwapRectangle( +#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"); @@ -351,11 +348,3 @@ void DisplayHardware::makeCurrent() const { eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); } - -void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const { - mDisplaySurface->copyFrontToImage(front); -} - -void DisplayHardware::copyBackToImage(const copybit_image_t& front) const { - mDisplaySurface->copyBackToImage(front); -} diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index 550a4d1..6914d0c 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -22,31 +22,36 @@ #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 copybit_device_t; +struct framebuffer_device_t; struct copybit_image_t; -struct copybit_t; namespace android { -class EGLDisplaySurface; +class FramebufferNativeWindow; class DisplayHardware : public DisplayHardwareBase { public: enum { DIRECT_TEXTURE = 0x00000002, - SWAP_RECTANGLE_EXTENSION= 0x00000004, COPY_BITS_EXTENSION = 0x00000008, NPOT_EXTENSION = 0x00000100, DRAW_TEXTURE_EXTENSION = 0x00000200, BUFFER_PRESERVED = 0x00010000, - UPDATE_ON_DEMAND = 0x00020000, // video driver feature + PARTIAL_UPDATES = 0x00020000, // video driver feature SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, + CACHED_BUFFERS = 0x00100000 }; DisplayHardware( @@ -73,15 +78,11 @@ public: void makeCurrent() const; uint32_t getPageFlipCount() const; - void getDisplaySurface(copybit_image_t* img) const; - void getDisplaySurface(GGLSurface* fb) const; EGLDisplay getEGLDisplay() const { return mDisplay; } - copybit_device_t* getBlitEngine() const { return mBlitEngine; } overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } - void copyFrontToImage(const copybit_image_t& front) const; - void copyBackToImage(const copybit_image_t& front) const; - + status_t compositionComplete() const; + Rect bounds() const { return Rect(mWidth, mHeight); } @@ -102,9 +103,9 @@ private: int mHeight; PixelFormat mFormat; uint32_t mFlags; - mutable Region mDirty; - sp<EGLDisplaySurface> mDisplaySurface; - copybit_device_t* mBlitEngine; + mutable uint32_t mPageFlipCount; + + sp<FramebufferNativeWindow> mNativeWindow; overlay_control_device_t* mOverlayEngine; }; diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index f75e5c2..1d09f84 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <assert.h> #include <errno.h> #include <stdlib.h> diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp deleted file mode 100644 index 7168bf2..0000000 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp +++ /dev/null @@ -1,585 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "SurfaceFlinger" - -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <math.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include <cutils/log.h> -#include <cutils/properties.h> - -#include <utils/IBinder.h> -#include <utils/MemoryDealer.h> -#include <utils/MemoryBase.h> -#include <utils/MemoryHeapPmem.h> -#include <utils/MemoryHeapBase.h> -#include <utils/IPCThreadState.h> -#include <utils/StopWatch.h> - -#include <ui/ISurfaceComposer.h> - -#include "VRamHeap.h" -#include "GPUHardware.h" - -#if HAVE_ANDROID_OS -#include <linux/android_pmem.h> -#endif - -#include "GPUHardware/GPUHardware.h" - - -/* - * Manage the GPU. This implementation is very specific to the G1. - * There are no abstraction here. - * - * All this code will soon go-away and be replaced by a new architecture - * for managing graphics accelerators. - * - * In the meantime, it is conceptually possible to instantiate a - * GPUHardwareInterface for another GPU (see GPUFactory at the bottom - * of this file); practically... doubtful. - * - */ - -namespace android { - -// --------------------------------------------------------------------------- - -class GPUClientHeap; -class GPUAreaHeap; - -class GPUHardware : public GPUHardwareInterface, public IBinder::DeathRecipient -{ -public: - static const int GPU_RESERVED_SIZE; - static const int GPUR_SIZE; - - GPUHardware(); - virtual ~GPUHardware(); - - virtual void revoke(int pid); - virtual sp<MemoryDealer> request(int pid); - virtual status_t request(int pid, - const sp<IGPUCallback>& callback, - ISurfaceComposer::gpu_info_t* gpu); - - virtual status_t friendlyRevoke(); - virtual void unconditionalRevoke(); - - virtual pid_t getOwner() const { return mOwner; } - - // used for debugging only... - virtual sp<SimpleBestFitAllocator> getAllocator() const; - -private: - - - enum { - NO_OWNER = -1, - }; - - struct GPUArea { - sp<GPUAreaHeap> heap; - sp<MemoryHeapPmem> clientHeap; - sp<IMemory> map(); - }; - - struct Client { - pid_t pid; - GPUArea smi; - GPUArea ebi; - GPUArea reg; - void createClientHeaps(); - void revokeAllHeaps(); - }; - - Client& getClientLocked(pid_t pid); - status_t requestLocked(int pid); - void releaseLocked(); - void takeBackGPULocked(); - void registerCallbackLocked(const sp<IGPUCallback>& callback, - Client& client); - - virtual void binderDied(const wp<IBinder>& who); - - mutable Mutex mLock; - sp<GPUAreaHeap> mSMIHeap; - sp<GPUAreaHeap> mEBIHeap; - sp<GPUAreaHeap> mREGHeap; - - KeyedVector<pid_t, Client> mClients; - DefaultKeyedVector< wp<IBinder>, pid_t > mRegisteredClients; - - pid_t mOwner; - - sp<MemoryDealer> mCurrentAllocator; - sp<IGPUCallback> mCallback; - - sp<SimpleBestFitAllocator> mAllocator; - - Condition mCondition; -}; - -// size reserved for GPU surfaces -// 1200 KB fits exactly: -// - two 320*480 16-bits double-buffered surfaces -// - one 320*480 32-bits double-buffered surface -// - one 320*240 16-bits double-buffered, 4x anti-aliased surface -const int GPUHardware::GPU_RESERVED_SIZE = 1200 * 1024; -const int GPUHardware::GPUR_SIZE = 1 * 1024 * 1024; - -// --------------------------------------------------------------------------- - -/* - * GPUHandle is a special IMemory given to the client. It represents their - * handle to the GPU. Once they give it up, they loose GPU access, or if - * they explicitly revoke their access through the binder code 1000. - * In both cases, this triggers a callback to revoke() - * first, and then actually powers down the chip. - * - * In the case of a misbehaving app, GPUHardware can ask for an immediate - * release of the GPU to the target process which should answer by calling - * code 1000 on GPUHandle. If it doesn't in a timely manner, the GPU will - * be revoked from under their feet. - * - * We should never hold a strong reference on GPUHandle. In practice this - * shouldn't be a big issue though because clients should use code 1000 and - * not rely on the dtor being called. - * - */ - -class GPUClientHeap : public MemoryHeapPmem -{ -public: - GPUClientHeap(const wp<GPUHardware>& gpu, - const sp<MemoryHeapBase>& heap) - : MemoryHeapPmem(heap), mGPU(gpu) { } -protected: - wp<GPUHardware> mGPU; -}; - -class GPUAreaHeap : public MemoryHeapBase -{ -public: - GPUAreaHeap(const wp<GPUHardware>& gpu, - const char* const vram, size_t size=0, size_t reserved=0) - : MemoryHeapBase(vram, size), mGPU(gpu) { - if (base() != MAP_FAILED) { - if (reserved == 0) - reserved = virtualSize(); - mAllocator = new SimpleBestFitAllocator(reserved); - } - } - virtual sp<MemoryHeapPmem> createClientHeap() { - sp<MemoryHeapBase> parentHeap(this); - return new GPUClientHeap(mGPU, parentHeap); - } - virtual const sp<SimpleBestFitAllocator>& getAllocator() const { - return mAllocator; - } -private: - sp<SimpleBestFitAllocator> mAllocator; -protected: - wp<GPUHardware> mGPU; -}; - -class GPURegisterHeap : public GPUAreaHeap -{ -public: - GPURegisterHeap(const sp<GPUHardware>& gpu) - : GPUAreaHeap(gpu, "/dev/hw3d", GPUHardware::GPUR_SIZE) { } - virtual sp<MemoryHeapPmem> createClientHeap() { - sp<MemoryHeapBase> parentHeap(this); - return new MemoryHeapRegs(mGPU, parentHeap); - } -private: - class MemoryHeapRegs : public GPUClientHeap { - public: - MemoryHeapRegs(const wp<GPUHardware>& gpu, - const sp<MemoryHeapBase>& heap) - : GPUClientHeap(gpu, heap) { } - sp<MemoryHeapPmem::MemoryPmem> createMemory(size_t offset, size_t size); - virtual void revoke(); - private: - class GPUHandle : public MemoryHeapPmem::MemoryPmem { - public: - GPUHandle(const sp<GPUHardware>& gpu, - const sp<MemoryHeapPmem>& heap) - : MemoryHeapPmem::MemoryPmem(heap), - mGPU(gpu), mOwner(gpu->getOwner()) { } - virtual ~GPUHandle(); - virtual sp<IMemoryHeap> getMemory( - ssize_t* offset, size_t* size) const; - virtual void revoke() { }; - virtual status_t onTransact( - uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags); - private: - void revokeNotification(); - wp<GPUHardware> mGPU; - pid_t mOwner; - }; - }; -}; - -GPURegisterHeap::MemoryHeapRegs::GPUHandle::~GPUHandle() { - //LOGD("GPUHandle %p released, revoking GPU", this); - revokeNotification(); -} -void GPURegisterHeap::MemoryHeapRegs::GPUHandle::revokeNotification() { - sp<GPUHardware> hw(mGPU.promote()); - if (hw != 0) { - hw->revoke(mOwner); - } -} -sp<IMemoryHeap> GPURegisterHeap::MemoryHeapRegs::GPUHandle::getMemory( - ssize_t* offset, size_t* size) const -{ - sp<MemoryHeapPmem> heap = getHeap(); - if (offset) *offset = 0; - if (size) *size = heap !=0 ? heap->virtualSize() : 0; - return heap; -} -status_t GPURegisterHeap::MemoryHeapRegs::GPUHandle::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - status_t err = BnMemory::onTransact(code, data, reply, flags); - if (err == UNKNOWN_TRANSACTION && code == 1000) { - int callingPid = IPCThreadState::self()->getCallingPid(); - //LOGD("pid %d voluntarily revoking gpu", callingPid); - if (callingPid == mOwner) { - revokeNotification(); - // we've revoked the GPU, don't do it again later when we - // are destroyed. - mGPU.clear(); - } else { - LOGW("%d revoking someone else's gpu? (owner=%d)", - callingPid, mOwner); - } - err = NO_ERROR; - } - return err; -} - -// --------------------------------------------------------------------------- - - -sp<MemoryHeapPmem::MemoryPmem> GPURegisterHeap::MemoryHeapRegs::createMemory( - size_t offset, size_t size) -{ - sp<GPUHandle> memory; - sp<GPUHardware> gpu = mGPU.promote(); - if (heapID()>0 && gpu!=0) { -#if HAVE_ANDROID_OS - /* this is where the GPU is powered on and the registers are mapped - * in the client */ - //LOGD("ioctl(HW3D_GRANT_GPU)"); - int err = ioctl(heapID(), HW3D_GRANT_GPU, base()); - if (err) { - // it can happen if the master heap has been closed already - // in which case the GPU already is revoked (app crash for - // instance). - LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", - strerror(errno), heapID(), base()); - } - memory = new GPUHandle(gpu, this); -#endif - } - return memory; -} - -void GPURegisterHeap::MemoryHeapRegs::revoke() -{ - MemoryHeapPmem::revoke(); -#if HAVE_ANDROID_OS - if (heapID() > 0) { - //LOGD("ioctl(HW3D_REVOKE_GPU)"); - int err = ioctl(heapID(), HW3D_REVOKE_GPU, base()); - LOGE_IF(err, "HW3D_REVOKE_GPU failed (%s), mFD=%d, base=%p", - strerror(errno), heapID(), base()); - } -#endif -} - -/*****************************************************************************/ - -GPUHardware::GPUHardware() - : mOwner(NO_OWNER) -{ -} - -GPUHardware::~GPUHardware() -{ -} - -status_t GPUHardware::requestLocked(int pid) -{ - const int self_pid = getpid(); - if (pid == self_pid) { - // can't use GPU from surfaceflinger's process - return PERMISSION_DENIED; - } - - if (mOwner != pid) { - if (mREGHeap != 0) { - if (mOwner != NO_OWNER) { - // someone already has the gpu. - takeBackGPULocked(); - releaseLocked(); - } - } else { - // first time, initialize the stuff. - if (mSMIHeap == 0) - mSMIHeap = new GPUAreaHeap(this, "/dev/pmem_gpu0"); - if (mEBIHeap == 0) - mEBIHeap = new GPUAreaHeap(this, - "/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE); - mREGHeap = new GPURegisterHeap(this); - mAllocator = mEBIHeap->getAllocator(); - if (mAllocator == NULL) { - // something went terribly wrong. - mSMIHeap.clear(); - mEBIHeap.clear(); - mREGHeap.clear(); - return INVALID_OPERATION; - } - } - Client& client = getClientLocked(pid); - mCurrentAllocator = new MemoryDealer(client.ebi.clientHeap, mAllocator); - mOwner = pid; - } - return NO_ERROR; -} - -sp<MemoryDealer> GPUHardware::request(int pid) -{ - sp<MemoryDealer> dealer; - Mutex::Autolock _l(mLock); - Client* client; - LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); - if (requestLocked(pid) == NO_ERROR) { - dealer = mCurrentAllocator; - LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); - } - return dealer; -} - -status_t GPUHardware::request(int pid, const sp<IGPUCallback>& callback, - ISurfaceComposer::gpu_info_t* gpu) -{ - if (callback == 0) - return BAD_VALUE; - - sp<IMemory> gpuHandle; - LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner); - Mutex::Autolock _l(mLock); - status_t err = requestLocked(pid); - if (err == NO_ERROR) { - // it's guaranteed to be there, be construction - Client& client = mClients.editValueFor(pid); - registerCallbackLocked(callback, client); - gpu->count = 2; - gpu->regions[0].region = client.smi.map(); - gpu->regions[1].region = client.ebi.map(); - gpu->regs = client.reg.map(); - gpu->regions[0].reserved = 0; - gpu->regions[1].reserved = GPU_RESERVED_SIZE; - if (gpu->regs != 0) { - //LOGD("gpu core granted to pid %d, handle base=%p", - // mOwner, gpu->regs->pointer()); - } - mCallback = callback; - } else { - LOGW("couldn't grant gpu core to pid %d", pid); - } - return err; -} - -void GPUHardware::revoke(int pid) -{ - Mutex::Autolock _l(mLock); - if (mOwner > 0) { - if (pid != mOwner) { - LOGW("GPU owned by %d, revoke from %d", mOwner, pid); - return; - } - //LOGD("revoke pid=%d, owner=%d", pid, mOwner); - // mOwner could be <0 if the same process acquired the GPU - // several times without releasing it first. - mCondition.signal(); - releaseLocked(); - } -} - -status_t GPUHardware::friendlyRevoke() -{ - Mutex::Autolock _l(mLock); - //LOGD("friendlyRevoke owner=%d", mOwner); - takeBackGPULocked(); - releaseLocked(); - return NO_ERROR; -} - -void GPUHardware::takeBackGPULocked() -{ - sp<IGPUCallback> callback = mCallback; - mCallback.clear(); - if (callback != 0) { - callback->gpuLost(); // one-way - mCondition.waitRelative(mLock, ms2ns(250)); - } -} - -void GPUHardware::releaseLocked() -{ - //LOGD("revoking gpu from pid %d", mOwner); - if (mOwner != NO_OWNER) { - // this may fail because the client might have died, and have - // been removed from the list. - ssize_t index = mClients.indexOfKey(mOwner); - if (index >= 0) { - Client& client(mClients.editValueAt(index)); - client.revokeAllHeaps(); - } - mOwner = NO_OWNER; - mCurrentAllocator.clear(); - mCallback.clear(); - } -} - -GPUHardware::Client& GPUHardware::getClientLocked(pid_t pid) -{ - ssize_t index = mClients.indexOfKey(pid); - if (index < 0) { - Client client; - client.pid = pid; - client.smi.heap = mSMIHeap; - client.ebi.heap = mEBIHeap; - client.reg.heap = mREGHeap; - index = mClients.add(pid, client); - } - Client& client(mClients.editValueAt(index)); - client.createClientHeaps(); - return client; -} - -// ---------------------------------------------------------------------------- -// for debugging / testing ... - -sp<SimpleBestFitAllocator> GPUHardware::getAllocator() const { - Mutex::Autolock _l(mLock); - return mAllocator; -} - -void GPUHardware::unconditionalRevoke() -{ - Mutex::Autolock _l(mLock); - releaseLocked(); -} - -// --------------------------------------------------------------------------- - -sp<IMemory> GPUHardware::GPUArea::map() { - sp<IMemory> memory; - if (clientHeap != 0 && heap != 0) { - memory = clientHeap->mapMemory(0, heap->virtualSize()); - } - return memory; -} - -void GPUHardware::Client::createClientHeaps() -{ - if (smi.clientHeap == 0) - smi.clientHeap = smi.heap->createClientHeap(); - if (ebi.clientHeap == 0) - ebi.clientHeap = ebi.heap->createClientHeap(); - if (reg.clientHeap == 0) - reg.clientHeap = reg.heap->createClientHeap(); -} - -void GPUHardware::Client::revokeAllHeaps() -{ - if (smi.clientHeap != 0) - smi.clientHeap->revoke(); - if (ebi.clientHeap != 0) - ebi.clientHeap->revoke(); - if (reg.clientHeap != 0) - reg.clientHeap->revoke(); -} - -void GPUHardware::registerCallbackLocked(const sp<IGPUCallback>& callback, - Client& client) -{ - sp<IBinder> binder = callback->asBinder(); - if (mRegisteredClients.add(binder, client.pid) >= 0) { - binder->linkToDeath(this); - } -} - -void GPUHardware::binderDied(const wp<IBinder>& who) -{ - Mutex::Autolock _l(mLock); - pid_t pid = mRegisteredClients.valueFor(who); - if (pid != 0) { - ssize_t index = mClients.indexOfKey(pid); - if (index >= 0) { - //LOGD("*** removing client at %d", index); - Client& client(mClients.editValueAt(index)); - client.revokeAllHeaps(); // not really needed in theory - mClients.removeItemsAt(index); - if (mClients.size() == 0) { - //LOGD("*** was last client closing everything"); - mCallback.clear(); - mAllocator.clear(); - mCurrentAllocator.clear(); - mSMIHeap.clear(); - mREGHeap.clear(); - - // NOTE: we cannot clear the EBI heap because surfaceflinger - // itself may be using it, since this is where surfaces - // are allocated. if we're in the middle of compositing - // a surface (even if its process just died), we cannot - // rip the heap under our feet. - - mOwner = NO_OWNER; - } - } - } -} - -// --------------------------------------------------------------------------- - -sp<GPUHardwareInterface> GPUFactory::getGPU() -{ - sp<GPUHardwareInterface> gpu; - if (access("/dev/hw3d", F_OK) == 0) { - gpu = new GPUHardware(); - } - return gpu; -} - -// --------------------------------------------------------------------------- -}; // namespace android - diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/GPUHardware/GPUHardware.h deleted file mode 100644 index 3354528..0000000 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 ANDROID_GPU_HARDWARE_H -#define ANDROID_GPU_HARDWARE_H - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/RefBase.h> -#include <utils/threads.h> -#include <utils/KeyedVector.h> - -#include <ui/ISurfaceComposer.h> - -namespace android { - -// --------------------------------------------------------------------------- - -class IGPUCallback; - -class GPUHardwareInterface : public virtual RefBase -{ -public: - virtual void revoke(int pid) = 0; - virtual sp<MemoryDealer> request(int pid) = 0; - virtual status_t request(int pid, const sp<IGPUCallback>& callback, - ISurfaceComposer::gpu_info_t* gpu) = 0; - - virtual status_t friendlyRevoke() = 0; - - // used for debugging only... - virtual sp<SimpleBestFitAllocator> getAllocator() const = 0; - virtual pid_t getOwner() const = 0; - virtual void unconditionalRevoke() = 0; -}; - -// --------------------------------------------------------------------------- - -class GPUFactory -{ -public: - // the gpu factory - static sp<GPUHardwareInterface> getGPU(); -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_GPU_HARDWARE_H diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 96395a8..f5a5a0b 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -14,26 +14,24 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #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 <ui/EGLDisplaySurface.h> +#include <ui/Surface.h> #include "clz.h" #include "Layer.h" -#include "LayerBitmap.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" @@ -49,292 +47,375 @@ const char* const Layer::typeID = "Layer"; // --------------------------------------------------------------------------- -Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& c, int32_t i) : LayerBaseClient(flinger, display, c, i), mSecure(false), - mFrontBufferIndex(1), + mNoEGLImageForSwBuffers(false), mNeedsBlending(true), - mResizeTransactionDone(false), - mTextureName(-1U), mTextureWidth(0), mTextureHeight(0) + mNeedsDithering(false) { // no OpenGL operation is possible here, since we might not be // in the OpenGL thread. + mFrontBufferIndex = lcblk->getFrontBuffer(); } Layer::~Layer() { - client->free(clientIndex()); - // this should always be called from the OpenGL thread - if (mTextureName != -1U) { - //glDeleteTextures(1, &mTextureName); - deletedTextures.add(mTextureName); - } + destroy(); + // the actual buffers will be destroyed here } -void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) +void Layer::destroy() { - LayerBase::initStates(w,h,flags); - - if (flags & ISurfaceComposer::eDestroyBackbuffer) - lcblk->flags |= eNoCopyBack; + 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::getSurface() const +sp<LayerBaseClient::Surface> Layer::createSurface() const { return mSurface; } -status_t Layer::setBuffers( Client* client, - uint32_t w, uint32_t h, +status_t Layer::ditch() +{ + // the layer is not on screen anymore. free as much resources as possible + 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; - // TODO: if eHardware is explicitly requested, we should fail - // on systems where we can't allocate memory that can be used with - // DMA engines for instance. - - // FIXME: we always ask for hardware for now (this should come from copybit) - flags |= ISurfaceComposer::eHardware; - - const uint32_t memory_flags = flags & - (ISurfaceComposer::eGPU | - ISurfaceComposer::eHardware | - ISurfaceComposer::eSecure); + // the display's pixel format + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + PixelFormatInfo displayInfo; + getPixelFormatInfo(hw.getFormat(), &displayInfo); + const uint32_t hwFlags = hw.getFlags(); - // pixel-alignment. the final alignment may be bigger because - // we always force a 4-byte aligned bpr. - uint32_t alignment = 1; - - if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) { - // FIXME: this value should come from the h/w - alignment = 8; - // FIXME: this is msm7201A specific, as its GPU only supports - // BGRA_8888. - if (format == PIXEL_FORMAT_RGBA_8888) { - format = PIXEL_FORMAT_BGRA_8888; - } - } - + mFormat = format; + mWidth = w; + mHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; - sp<MemoryDealer> allocators[2]; - for (int i=0 ; i<2 ; i++) { - allocators[i] = client->createAllocator(memory_flags); - if (allocators[i] == 0) - return NO_MEMORY; - mBuffers[i].init(allocators[i]); - int err = mBuffers[i].setBits(w, h, alignment, format, LayerBitmap::SECURE_BITS); - if (err != NO_ERROR) - return err; - mBuffers[i].clear(); // clear the bits for security - mBuffers[i].getInfo(lcblk->surface + i); - } - - mSurface = new Surface(clientIndex(), - allocators[0]->getMemoryHeap(), - allocators[1]->getMemoryHeap(), - mIdentity); + 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) { - if (UNLIKELY(mTextureName == -1U)) { - // create the texture name the first time - // can't do that in the ctor, because it runs in another thread. - mTextureName = createTexture(); + Mutex::Autolock _l(mLock); + sp<GraphicBuffer> buffer(getFrontBufferLocked()); + 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; } - const GGLSurface& t(frontBuffer().surface()); - loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight); -} +#ifdef EGL_ANDROID_image_native_buffer + if (mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { + if (mTextures[index].dirty) { + initializeEglImage(buffer, &mTextures[index]); + } + } 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); + initializeEglImage( + mHybridBuffer, &mTextures[0]); + } + + 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 GGL_PIXEL_FORMAT_RGB_565: + case GGL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + case GGL_PIXEL_FORMAT_RGBA_8888: + case GGL_PIXEL_FORMAT_RGBX_8888: + bpp = 4; + break; + case GGL_PIXEL_FORMAT_YCbCr_422_SP: + case GGL_PIXEL_FORMAT_YCbCr_420_SP: + // just show the Y plane of YUV buffers + bpp = 1; + break; + default: + // 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 + { + 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 { - if (UNLIKELY(mTextureName == -1LU)) { - //LOGW("Layer %p doesn't have a texture", this); + 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. clearWithOpenGL(clip); return; } + drawWithOpenGL(clip, mTextures[index]); +} - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - const LayerBitmap& front(frontBuffer()); - const GGLSurface& t(front.surface()); - - status_t err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - // StopWatch watch("copybit"); - const State& s(drawingState()); - - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); - - copybit_image_t src; - front.getBitmapSurface(&src); - copybit_rect_t srect = { 0, 0, t.width, t.height }; - - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - s.flags & ISurfaceComposer::eLayerDither ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - - region_iterator it(clip); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); +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; } - if (!can_use_copybit || err) { - drawWithOpenGL(clip, mTextureName, t); + 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(); } -} -status_t Layer::reallocateBuffer(int32_t index, uint32_t w, uint32_t h) -{ - LOGD_IF(DEBUG_RESIZE, - "reallocateBuffer (layer=%p), " - "requested (%dx%d), " - "index=%d, (%dx%d), (%dx%d)", - this, - int(w), int(h), - int(index), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - - status_t err = mBuffers[index].resize(w, h); - if (err == NO_ERROR) { - mBuffers[index].getInfo(lcblk->surface + index); + const uint32_t effectiveUsage = getEffectiveUsage(usage); + if (buffer!=0 && buffer->getStrongCount() == 1) { + err = buffer->reallocate(w, h, mFormat, effectiveUsage); } else { - LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", - index, w, h, err, strerror(err)); - // XXX: what to do, what to do? We could try to free some - // hidden surfaces, instead of killing this one? + // 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(); } - return err; -} -uint32_t Layer::doTransaction(uint32_t flags) -{ - const Layer::State& front(drawingState()); - const Layer::State& temp(currentState()); + 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); + } - // the test front.{w|h} != temp.{w|h} is not enough because it is possible - // that the size changed back to its previous value before the buffer - // was resized (in the eLocked case below), in which case, we still - // need to execute the code below so the clients have a chance to be - // release. resze() deals with the fact that the size can be the same. + 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 +{ /* - * Various states we could be in... - - resize = state & eResizeRequested; - if (backbufferChanged) { - if (resize == 0) { - // ERROR, the resized buffer doesn't have its resize flag set - } else if (resize == mask) { - // ERROR one of the buffer has already been resized - } else if (resize == mask ^ eResizeRequested) { - // ERROR, the resized buffer doesn't have its resize flag set - } else if (resize == eResizeRequested) { - // OK, Normal case, proceed with resize - } - } else { - if (resize == 0) { - // OK, nothing special, do nothing - } else if (resize == mask) { - // restarted transaction, do nothing - } else if (resize == mask ^ eResizeRequested) { - // restarted transaction, do nothing - } else if (resize == eResizeRequested) { - // OK, size reset to previous value, proceed with resize - } - } + * 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 + * */ - // Index of the back buffer - const bool backbufferChanged = (front.w != temp.w) || (front.h != temp.h); - const uint32_t state = lcblk->swapState; - const int32_t clientBackBufferIndex = layer_cblk_t::backBuffer(state); - const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; - uint32_t resizeFlags = state & eResizeRequested; - - if (UNLIKELY(backbufferChanged && (resizeFlags != eResizeRequested))) { - LOGE( "backbuffer size changed, but both resize flags are not set! " - "(layer=%p), state=%08x, requested (%dx%d), drawing (%d,%d), " - "index=%d, (%dx%d), (%dx%d)", - this, state, - int(temp.w), int(temp.h), - int(drawingState().w), int(drawingState().h), - int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - // if we get there we're pretty screwed. the only reasonable - // thing to do is to pretend we should do the resize since - // backbufferChanged is set (this also will give a chance to - // client to get unblocked) - resizeFlags = eResizeRequested; + 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; +} - if (resizeFlags == eResizeRequested) { - // NOTE: asserting that clientBackBufferIndex!=mFrontBufferIndex - // here, would be wrong and misleading because by this point - // mFrontBufferIndex has not been updated yet. +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), state=%08x, " - "requested (%dx%d), " - "drawing (%d,%d), " - "index=%d, (%dx%d), (%dx%d)", - this, state, - int(temp.w), int(temp.h), - int(drawingState().w), int(drawingState().h), - int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - - if (state & eLocked) { - // if the buffer is locked, we can't resize anything because - // - the backbuffer is currently in use by the user - // - the front buffer is being shown - // We just act as if the transaction didn't happen and we - // reschedule it later... - flags |= eRestartTransaction; - } else { - // This buffer needs to be resized - status_t err = - resize(clientBackBufferIndex, temp.w, temp.h, "transaction"); - if (err == NO_ERROR) { - const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; - android_atomic_and(~mask, &(lcblk->swapState)); - // since a buffer became available, we can let the client go... - mFlinger->scheduleBroadcast(client); - mResizeTransactionDone = true; - - // 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(); - } - } + "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 @@ -346,54 +427,10 @@ uint32_t Layer::doTransaction(uint32_t flags) return LayerBase::doTransaction(flags); } -status_t Layer::resize( - int32_t clientBackBufferIndex, - uint32_t width, uint32_t height, - const char* what) -{ - /* - * handle resize (backbuffer and frontbuffer reallocation) - */ - - const LayerBitmap& clientBackBuffer(mBuffers[clientBackBufferIndex]); - - // if the new (transaction) size is != from the the backbuffer - // then we need to reallocate the backbuffer - bool backbufferChanged = (clientBackBuffer.width() != width) || - (clientBackBuffer.height() != height); - - LOGD_IF(!backbufferChanged, - "(%s) eResizeRequested (layer=%p), but size not changed: " - "requested (%dx%d), drawing (%d,%d), current (%d,%d)," - "state=%08lx, index=%d, (%dx%d), (%dx%d)", - what, this, - int(width), int(height), - int(drawingState().w), int(drawingState().h), - int(currentState().w), int(currentState().h), - long(lcblk->swapState), - int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - - // this can happen when changing the size back and forth quickly - status_t err = NO_ERROR; - if (backbufferChanged) { - err = reallocateBuffer(clientBackBufferIndex, width, height); - } - if (UNLIKELY(err != NO_ERROR)) { - // couldn't reallocate the surface - android_atomic_write(eInvalidSurface, &lcblk->swapState); - memset(lcblk->surface+clientBackBufferIndex, 0, sizeof(surface_info_t)); - } - return err; -} - -void Layer::setSizeChanged(uint32_t w, uint32_t h) -{ - LOGD_IF(DEBUG_RESIZE, - "setSizeChanged w=%d, h=%d (old: w=%d, h=%d)", - w, h, mCurrentState.w, mCurrentState.h); - android_atomic_or(eResizeRequested, &(lcblk->swapState)); +void Layer::setDrawingSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + mWidth = w; + mHeight = h; } // ---------------------------------------------------------------------------- @@ -402,128 +439,61 @@ void Layer::setSizeChanged(uint32_t w, uint32_t h) void Layer::lockPageFlip(bool& recomputeVisibleRegions) { - uint32_t state = android_atomic_or(eBusy, &(lcblk->swapState)); - // preemptively block the client, because he might set - // eFlipRequested at any time and want to use this buffer - // for the next frame. This will be unset below if it - // turns out we didn't need it. - - uint32_t mask = eInvalidSurface | eFlipRequested | eResizeRequested; - if (!(state & mask)) - return; - - if (UNLIKELY(state & eInvalidSurface)) { - // if eInvalidSurface is set, this means the surface - // became invalid during a transaction (NO_MEMORY for instance) - mFlinger->scheduleBroadcast(client); + 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; } + + // we retired a buffer, which becomes the new front buffer + mFrontBufferIndex = buf; - if (UNLIKELY(state & eFlipRequested)) { - uint32_t oldState; - mPostedDirtyRegion = post(&oldState, recomputeVisibleRegions); - if (oldState & eNextFlipPending) { - // Process another round (we know at least a buffer - // is ready for that client). - mFlinger->signalEvent(); - } - } -} - -Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) -{ - // atomically swap buffers and (re)set eFlipRequested - int32_t oldValue, newValue; - layer_cblk_t * const lcblk = this->lcblk; - do { - oldValue = lcblk->swapState; - // get the current value - - LOG_ASSERT(oldValue&eFlipRequested, - "eFlipRequested not set, yet we're flipping! (state=0x%08lx)", - long(oldValue)); - - newValue = (oldValue ^ eIndex); - // swap buffers - - newValue &= ~(eFlipRequested | eNextFlipPending); - // clear eFlipRequested and eNextFlipPending - - if (oldValue & eNextFlipPending) - newValue |= eFlipRequested; - // if eNextFlipPending is set (second buffer already has something - // in it) we need to reset eFlipRequested because the client - // might never do it - - } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); - *previousSate = oldValue; - - const int32_t index = (newValue & eIndex) ^ 1; - mFrontBufferIndex = index; - - // ... post the new front-buffer - Region dirty(lcblk->region + index); - dirty.andSelf(frontBuffer().bounds()); - - //LOGI("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n", - // oldValue, newValue, mFrontBufferIndex); - //dirty.dump("dirty"); - - if (UNLIKELY(oldValue & eResizeRequested)) { + // get the dirty region + sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); + const Region dirty(lcblk->getDirtyRegion(buf)); + mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); - LOGD_IF(DEBUG_RESIZE, - "post (layer=%p), state=%08x, " - "index=%d, (%dx%d), (%dx%d)", - this, newValue, - int(1-index), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - - // here, we just posted the surface and we have resolved - // the front/back buffer indices. The client is blocked, so - // it cannot start using the new backbuffer. - - // If the backbuffer was resized in THIS round, we actually cannot - // resize the frontbuffer because it has *just* been drawn (and we - // would have nothing to draw). In this case we just skip the resize - // it'll happen after the next page flip or during the next - // transaction. - - const uint32_t mask = (1-index) ? eResizeBuffer1 : eResizeBuffer0; - if (mResizeTransactionDone && (newValue & mask)) { - // Resize the layer's second buffer only if the transaction - // happened. It may not have happened yet if eResizeRequested - // was set immediately after the "transactionRequested" test, - // in which case the drawing state's size would be wrong. - mFreezeLock.clear(); - const Layer::State& s(drawingState()); - if (resize(1-index, s.w, s.h, "post") == NO_ERROR) { - do { - oldValue = lcblk->swapState; - if ((oldValue & eResizeRequested) == eResizeRequested) { - // ugh, another resize was requested since we processed - // the first buffer, don't free the client, and let - // the next transaction handle everything. - break; - } - newValue = oldValue & ~mask; - } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); - } - mResizeTransactionDone = false; + 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; - this->contentDirty = true; } - } - reloadTexture(dirty); + // we now have the correct size, unfreeze the screen + mFreezeLock.clear(); + } - return dirty; -} + if (lcblk->getQueuedCount()) { + // signal an event if we have more buffers waiting + mFlinger->signalEvent(); + } -Point Layer::getPhysicalSize() const -{ - const LayerBitmap& front(frontBuffer()); - return Point(front.width(), front.height()); + if (!mPostedDirtyRegion.isEmpty()) { + reloadTexture( mPostedDirtyRegion ); + } } void Layer::unlockPageFlip( @@ -544,23 +514,42 @@ void Layer::unlockPageFlip( // is in screen space as well). dirtyRegion.andSelf(visibleRegionScreen); outDirtyRegion.orSelf(dirtyRegion); - - // client could be blocked, so signal them so they get a - // chance to reevaluate their condition. - mFlinger->scheduleBroadcast(client); } } void Layer::finishPageFlip() { - if (LIKELY(!(lcblk->swapState & eInvalidSurface))) { - LOGE_IF(!(lcblk->swapState & eBusy), - "layer %p wasn't locked!", this); - android_atomic_and(~eBusy, &(lcblk->swapState)); - } - mFlinger->scheduleBroadcast(client); + 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; +} // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index 2867f2b..1310ecc 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -20,14 +20,15 @@ #include <stdint.h> #include <sys/types.h> +#include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> - -#include <private/ui/SharedState.h> -#include <private/ui/LayerState.h> - #include <pixelflinger/pixelflinger.h> -#include "LayerBitmap.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + #include "LayerBase.h" #include "Transform.h" @@ -36,12 +37,12 @@ namespace android { // --------------------------------------------------------------------------- class Client; -class LayerBitmap; -class MemoryDealer; class FreezeLock; // --------------------------------------------------------------------------- +const size_t NUM_BUFFERS = 2; + class Layer : public LayerBaseClient { public: @@ -49,68 +50,79 @@ public: static const char* const typeID; virtual char const* getTypeID() const { return typeID; } virtual uint32_t getTypeInfo() const { return typeInfo; } - + Layer(SurfaceFlinger* flinger, DisplayID display, - Client* c, int32_t i); + const sp<Client>& client, int32_t i); virtual ~Layer(); - inline PixelFormat pixelFormat() const { - return frontBuffer().pixelFormat(); - } + status_t setBuffers(uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); - status_t setBuffers( Client* client, - 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 void initStates(uint32_t w, uint32_t h, uint32_t flags); - virtual void setSizeChanged(uint32_t w, uint32_t h); virtual uint32_t doTransaction(uint32_t transactionFlags); - virtual Point getPhysicalSize() const; 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 GLuint getTextureName() const { return mTextureName; } - virtual sp<Surface> getSurface() const; - - const LayerBitmap& getBuffer(int i) const { return mBuffers[i]; } - LayerBitmap& getBuffer(int i) { return mBuffers[i]; } - + virtual sp<Surface> createSurface() const; + virtual status_t ditch(); + + // only for debugging + inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } // only for debugging - const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + // only for debugging + inline PixelFormat pixelFormat() const { return mFormat; } private: - inline const LayerBitmap& - frontBuffer() const { return getBuffer(mFrontBufferIndex); } - inline LayerBitmap& - frontBuffer() { return getBuffer(mFrontBufferIndex); } - inline const LayerBitmap& - backBuffer() const { return getBuffer(1-mFrontBufferIndex); } - inline LayerBitmap& - backBuffer() { return getBuffer(1-mFrontBufferIndex); } - + inline sp<GraphicBuffer> getFrontBufferLocked() { + return mBuffers[mFrontBufferIndex]; + } + void reloadTexture(const Region& dirty); - status_t resize(int32_t index, uint32_t w, uint32_t h, const char* what); - Region post(uint32_t* oldState, bool& recomputeVisibleRegions); - status_t reallocateBuffer(int32_t index, uint32_t w, uint32_t h); - + 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; - LayerBitmap mBuffers[2]; + bool mNoEGLImageForSwBuffers; int32_t mFrontBufferIndex; bool mNeedsBlending; - bool mResizeTransactionDone; + 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; - GLuint mTextureName; - GLuint mTextureWidth; - GLuint mTextureHeight; + mutable Mutex mLock; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index 0cf53f7..8003d22 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #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> @@ -30,17 +30,10 @@ #include "clz.h" #include "LayerBase.h" -#include "LayerBlur.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" -// We don't honor the premultiplied alpha flags, which means that -// premultiplied surface may be composed using a non-premultiplied -// equation. We do this because it may be a lot faster on some hardware -// The correct value is HONOR_PREMULTIPLIED_ALPHA = 1 -#define HONOR_PREMULTIPLIED_ALPHA 0 - namespace android { // --------------------------------------------------------------------------- @@ -53,19 +46,14 @@ const char* const LayerBaseClient::typeID = "LayerBaseClient"; // --------------------------------------------------------------------------- -Vector<GLuint> LayerBase::deletedTextures; - -int32_t LayerBase::sIdentity = 0; - LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), mFlinger(flinger), mTransformed(false), + mUseLinearFiltering(false), mOrientation(0), - mCanUseCopyBit(false), mTransactionFlags(0), mPremultipliedAlpha(true), - mIdentity(uint32_t(android_atomic_inc(&sIdentity))), mInvalidate(0) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); @@ -95,26 +83,22 @@ void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags) if (flags & ISurfaceComposer::eNonPremultiplied) mPremultipliedAlpha = false; - mCurrentState.z = 0; - mCurrentState.w = w; - mCurrentState.h = h; - mCurrentState.alpha = 0xFF; - mCurrentState.flags = layerFlags; - mCurrentState.sequence = 0; + 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(bool skipSize) { - const uint32_t w = mDrawingState.w; - const uint32_t h = mDrawingState.h; +void LayerBase::commitTransaction() { mDrawingState = mCurrentState; - if (skipSize) { - mDrawingState.w = w; - mDrawingState.h = h; - } } void LayerBase::forceVisibilityTransaction() { // this can be called without SurfaceFlinger.mStateLock, but if we @@ -133,9 +117,6 @@ uint32_t LayerBase::setTransactionFlags(uint32_t flags) { return android_atomic_or(flags, &mTransactionFlags); } -void LayerBase::setSizeChanged(uint32_t w, uint32_t h) { -} - bool LayerBase::setPosition(int32_t x, int32_t y) { if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false; @@ -153,11 +134,10 @@ bool LayerBase::setLayer(uint32_t z) { return true; } bool LayerBase::setSize(uint32_t w, uint32_t h) { - if (mCurrentState.w == w && mCurrentState.h == h) + if (mCurrentState.requested_w == w && mCurrentState.requested_h == h) return false; - setSizeChanged(w, h); - mCurrentState.w = w; - mCurrentState.h = h; + mCurrentState.requested_w = w; + mCurrentState.requested_h = h; requestTransaction(); return true; } @@ -214,32 +194,49 @@ 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; + this->contentDirty = true; + } + 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(flags & eRestartTransaction); + commitTransaction(); return flags; } -Point LayerBase::getPhysicalSize() const -{ - const Layer::State& front(drawingState()); - return Point(front.w, front.h); -} - void LayerBase::validateVisibility(const Transform& planeTransform) { const Layer::State& s(drawingState()); const Transform tr(planeTransform * s.transform); const bool transformed = tr.transformed(); - const Point size(getPhysicalSize()); - uint32_t w = size.x; - uint32_t h = size.y; + 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); @@ -265,43 +262,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform) mTransformed = transformed; mLeft = tr.tx(); mTop = tr.ty(); - - // see if we can/should use 2D h/w with the new configuration - mCanUseCopyBit = false; - copybit_device_t* copybit = mFlinger->getBlitEngine(); - if (copybit) { - const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG); - const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS); - mCanUseCopyBit = true; - if ((mOrientation < 0) && (step > 1)) { - // arbitrary orientations not supported - mCanUseCopyBit = false; - } else if ((mOrientation > 0) && (step > 90)) { - // 90 deg rotations not supported - mCanUseCopyBit = false; - } else if ((tr.getType() & SkMatrix::kScale_Mask) && (scaleBits < 12)) { - // arbitrary scaling not supported - mCanUseCopyBit = false; - } -#if HONOR_PREMULTIPLIED_ALPHA - else if (needsBlending() && mPremultipliedAlpha) { - // pre-multiplied alpha not supported - mCanUseCopyBit = false; - } -#endif - else { - // here, we determined we can use copybit - if (tr.getType() & SkMatrix::kScale_Mask) { - // and we have scaling - if (!transparentRegionScreen.isRect()) { - // we punt because blending is cheap (h/w) and the region is - // complex, which may causes artifacts when copying - // scaled content - transparentRegionScreen.clear(); - } - } - } - } } void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) @@ -329,8 +289,9 @@ void LayerBase::invalidate() void LayerBase::drawRegion(const Region& reg) const { - Region::iterator iterator(reg); - if (iterator) { + 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(); @@ -338,7 +299,8 @@ void LayerBase::drawRegion(const Region& reg) const const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, { fbWidth, fbHeight }, { 0, fbHeight } }; glVertexPointer(2, GL_SHORT, 0, vertices); - while (iterator.iterate(&r)) { + 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); @@ -385,55 +347,52 @@ GLuint LayerBase::createTexture() const 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); - if (mFlags & DisplayHardware::SLOW_CONFIG) { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } + 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) const +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(0,0,0,0); + glColor4x(red,green,blue,alpha); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); - Rect r; - Region::iterator iterator(clip); - if (iterator) { - glEnable(GL_SCISSOR_TEST); - glVertexPointer(2, GL_FIXED, 0, mVertices); - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + + 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::drawWithOpenGL(const Region& clip, - GLint textureName, const GGLSurface& t, int transform) const +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(textureName); + validateTexture(texture.name); + uint32_t width = texture.width; + uint32_t height = texture.height; + glEnable(GL_TEXTURE_2D); - // Dithering... - if (s.flags & ISurfaceComposer::eLayerDither) { - glEnable(GL_DITHER); - } else { - glDisable(GL_DITHER); - } - if (UNLIKELY(s.alpha < 0xFF)) { // We have an alpha-modulation. We need to modulate all // texture components by alpha because we're always using @@ -468,77 +427,55 @@ void LayerBase::drawWithOpenGL(const Region& clip, } } + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); if (UNLIKELY(transformed() || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { //StopWatch watch("GL transformed"); - Region::iterator iterator(clip); - if (iterator) { - // always use high-quality filtering with fast configurations - bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG); - if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - const GLfixed texCoords[4][2] = { - { 0, 0 }, - { 0, 0x10000 }, - { 0x10000, 0x10000 }, - { 0x10000, 0 } - }; - - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - - if (transform == HAL_TRANSFORM_ROT_90) { - glTranslatef(0, 1, 0); - glRotatef(-90, 0, 0, 1); - } - - if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { - // find the smallest power-of-two that will accommodate our surface - GLuint tw = 1 << (31 - clz(t.width)); - GLuint th = 1 << (31 - clz(t.height)); - if (tw < t.width) tw <<= 1; - if (th < t.height) th <<= 1; - // this divide should be relatively fast because it's - // a power-of-two (optimized path in libgcc) - GLfloat ws = GLfloat(t.width) /tw; - GLfloat hs = GLfloat(t.height)/th; - glScalef(ws, hs, 1.0f); - } - - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glVertexPointer(2, GL_FIXED, 0, mVertices); - glTexCoordPointer(2, GL_FIXED, 0, texCoords); + const GLfixed texCoords[4][2] = { + { 0, 0 }, + { 0, 0x10000 }, + { 0x10000, 0x10000 }, + { 0x10000, 0 } + }; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + // the texture's source is rotated + if (texture.transform == HAL_TRANSFORM_ROT_90) { + // TODO: handle the other orientations + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + } + + if (texture.NPOTAdjust) { + glScalef(texture.wScale, texture.hScale, 1.0f); + } - Rect r; - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, texCoords); - if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + 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 { - Region::iterator iterator(clip); - if (iterator) { - Rect r; - GLint crop[4] = { 0, t.height, t.width, -t.height }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - int x = tx(); - int y = ty(); - y = fbHeight - (y + t.height); - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, t.width, t.height); - } + GLint crop[4] = { 0, height, width, -height }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + int x = tx(); + int y = ty(); + y = fbHeight - (y + height); + 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, width, height); } } } @@ -548,25 +485,34 @@ 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); + } } -void LayerBase::loadTexture(const Region& dirty, - GLint textureName, const GGLSurface& t, - GLuint& textureWidth, GLuint& textureHeight) const +void LayerBase::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) const { - // TODO: defer the actual texture reload until LayerBase::validateTexture - // is called. - - uint32_t flags = mFlags; - glBindTexture(GL_TEXTURE_2D, textureName); + if (texture->name == -1U) { + // uh? + return; + } - GLuint tw = t.width; - GLuint th = t.height; + glBindTexture(GL_TEXTURE_2D, texture->name); /* * In OpenGL ES we can't specify a stride with glTexImage2D (however, - * GL_UNPACK_ALIGNMENT is 4, which in essence allows a limited form of - * stride). + * 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). * @@ -579,161 +525,294 @@ void LayerBase::loadTexture(const Region& dirty, * * This should never be a problem with POT textures */ - - tw += (((t.stride - tw) * bytesPerPixel(t.format)) / 4); - + + 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; + } - GLuint texture_w = tw; - GLuint texture_h = th; - if (!(flags & DisplayHardware::NPOT_EXTENSION)) { + if (texture->NPOTAdjust) { // find the smallest power-of-two that will accommodate our surface - texture_w = 1 << (31 - clz(t.width)); - texture_h = 1 << (31 - clz(t.height)); - if (texture_w < t.width) texture_w <<= 1; - if (texture_h < t.height) texture_h <<= 1; - if (texture_w != tw || texture_h != th) { - // we can't use DIRECT_TEXTURE since we changed the size - // of the texture - flags &= ~DisplayHardware::DIRECT_TEXTURE; - } + 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; } - if (flags & DisplayHardware::DIRECT_TEXTURE) { - // here we're guaranteed that texture_{w|h} == t{w|h} + 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 == GGL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGB, tw, th, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t.data); + 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 == GGL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGBA, tw, th, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t.data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGBA, tw, th, 0, - GL_RGBA, GL_UNSIGNED_BYTE, t.data); - } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) { - // TODO: add GL_BGRA extension + 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 == GGL_PIXEL_FORMAT_RGBA_8888 || + t.format == GGL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // 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, try the regular path - goto regular; + // 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); } - textureWidth = tw; - textureHeight = th; - } else { -regular: - Rect bounds(dirty.bounds()); - GLvoid* data = 0; - if (texture_w!=textureWidth || texture_h!=textureHeight) { - // texture size changed, we need to create a new one - - if (!textureWidth || !textureHeight) { - // this is the first time, load the whole texture - if (texture_w==tw && texture_h==th) { - // we can do it one pass - data = t.data; - } else { - // we have to create the texture first because it - // doesn't match the size of the buffer - bounds.set(Rect(tw, th)); - } - } - - if (t.format == GGL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, texture_w, texture_h, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture_w, texture_h, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture_w, texture_h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || - t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { - // just show the Y plane of YUV buffers - data = t.data; - glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, texture_w, texture_h, 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, textureName, t.format); - textureName = -1; - } - textureWidth = texture_w; - textureHeight = texture_h; - } - if (!data && textureName>=0) { - if (t.format == GGL_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.width*2); - } else if (t.format == GGL_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.width*2); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.width*4); - } + } + if (!data) { + if (t.format == GGL_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 == GGL_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 == GGL_PIXEL_FORMAT_RGBA_8888 || + t.format == GGL_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 ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // 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); } } } -bool LayerBase::canUseCopybit() const +status_t LayerBase::initializeEglImage( + const sp<GraphicBuffer>& buffer, Texture* texture) { - return mCanUseCopyBit; + 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); + + LOGE_IF(texture->image == EGL_NO_IMAGE_KHR, + "eglCreateImageKHR() failed. err=0x%4x", + eglGetError()); + + 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)) { + // this failed, for instance, because we don't support NPOT. + // FIXME: do something! + LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " + "failed err=0x%04x", + this, texture->image, error); + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + err = INVALID_OPERATION; + } else { + // Everything went okay! + texture->NPOTAdjust = false; + texture->dirty = false; + texture->width = clientBuf->width; + texture->height = clientBuf->height; + } + } else { + err = INVALID_OPERATION; + } + return err; } + // --------------------------------------------------------------------------- +int32_t LayerBaseClient::sIdentity = 0; + LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, - Client* c, int32_t i) - : LayerBase(flinger, display), client(c), - lcblk( c ? &(c->ctrlblk->layers[i]) : 0 ), - mIndex(i) -{ - if (client) { - client->bindLayer(this, i); - - // Initialize this layer's control block - memset(this->lcblk, 0, sizeof(layer_cblk_t)); - this->lcblk->identity = mIdentity; - Region::writeEmpty(&(this->lcblk->region[0]), sizeof(flat_region_t)); - Region::writeEmpty(&(this->lcblk->region[1]), sizeof(flat_region_t)); + 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() { - if (client) { + sp<Client> client(this->client.promote()); + if (client != 0) { client->free(mIndex); } + delete lcblk; } -int32_t LayerBaseClient::serverIndex() const { - if (client) { +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() const +sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() { - return new Surface(clientIndex(), mIdentity); + 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) +{ + return NULL; +}; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index a020f44..ed07b3f 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -20,8 +20,14 @@ #include <stdint.h> #include <sys/types.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <private/ui/SharedBufferStack.h> #include <private/ui/LayerState.h> +#include <utils/RefBase.h> + #include <ui/Region.h> #include <ui/Overlay.h> @@ -33,14 +39,15 @@ namespace android { // --------------------------------------------------------------------------- -class SurfaceFlinger; class DisplayHardware; -class GraphicPlane; class Client; +class GraphicBuffer; +class GraphicPlane; +class SurfaceFlinger; // --------------------------------------------------------------------------- -class LayerBase +class LayerBase : public RefBase { // poor man's dynamic_cast below template<typename T> @@ -69,10 +76,7 @@ public: } - static Vector<GLuint> deletedTextures; - LayerBase(SurfaceFlinger* flinger, DisplayID display); - virtual ~LayerBase(); DisplayID dpy; mutable bool contentDirty; @@ -83,6 +87,8 @@ public: 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; @@ -102,7 +108,7 @@ public: bool setTransparentRegionHint(const Region& opaque); bool setFlags(uint8_t flags, uint8_t mask); - void commitTransaction(bool skipSize); + void commitTransaction(); bool requestTransaction(); void forceVisibilityTransaction(); @@ -133,11 +139,6 @@ public: virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); /** - * setSizeChanged - called when the *current* state's size is changed. - */ - virtual void setSizeChanged(uint32_t w, uint32_t h); - - /** * doTransaction - process the transaction. This is a good place to figure * out which attributes of the surface have changed. */ @@ -157,13 +158,6 @@ public: virtual void setCoveredRegion(const Region& coveredRegion); /** - * getPhysicalSize - returns the physical size of the drawing state of - * the surface. If the surface is backed by a bitmap, this is the size of - * the bitmap (as opposed to the size of the drawing state). - */ - virtual Point getPhysicalSize() const; - - /** * validateVisibility - cache a bunch of things */ virtual void validateVisibility(const Transform& globalTransform); @@ -195,27 +189,42 @@ public: 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 vns servers. + * screenshots or VNC servers. */ virtual bool isSecure() const { return false; } - enum { // flags for doTransaction() - eVisibleRegion = 0x00000002, - eRestartTransaction = 0x00000008 - }; + /** 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(LayerBase*const* layerA, LayerBase*const* layerB) { + static int compareCurrentStateZ( + sp<LayerBase> const * layerA, + sp<LayerBase> const * layerB) { return layerA[0]->currentState().z - layerB[0]->currentState().z; } @@ -229,28 +238,42 @@ protected: GLuint createTexture() const; - void drawWithOpenGL(const Region& clip, - GLint textureName, - const GGLSurface& surface, - int transform = 0) 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); - void loadTexture(const Region& dirty, - GLint textureName, const GGLSurface& t, - GLuint& textureWidth, GLuint& textureHeight) const; - - bool canUseCopybit() const; - SurfaceFlinger* mFlinger; + sp<SurfaceFlinger> mFlinger; uint32_t mFlags; // cached during validateVisibility() bool mTransformed; + bool mUseLinearFiltering; int32_t mOrientation; GLfixed mVertices[4][2]; Rect mTransformedBounds; - bool mCanUseCopyBit; int mLeft; int mTop; @@ -262,16 +285,16 @@ protected: // don't change, don't need a lock bool mPremultipliedAlpha; - // only read - const uint32_t mIdentity; - // atomic volatile int32_t mInvalidate; +protected: + virtual ~LayerBase(); + private: - void validateTexture(GLint textureName) const; - static int32_t sIdentity; + LayerBase(const LayerBase& rhs); + void validateTexture(GLint textureName) const; }; @@ -286,67 +309,67 @@ public: 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, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBaseClient(); + virtual void onFirstRef(); + const wp<Client> client; - Client* const client; - layer_cblk_t* const lcblk; - + inline uint32_t getIdentity() const { return mIdentity; } inline int32_t clientIndex() const { return mIndex; } int32_t serverIndex() const; - virtual sp<Surface> getSurface() const; - uint32_t getIdentity() const { return mIdentity; } + sp<Surface> getSurface(); + virtual sp<Surface> createSurface() const; + + virtual void onRemoved(); class Surface : public BnSurface { public: - Surface(SurfaceID id, int identity) { - mParams.token = id; - mParams.identity = identity; - } - Surface(SurfaceID id, - const sp<IMemoryHeap>& heap0, - const sp<IMemoryHeap>& heap1, - int identity) - { - mParams.token = id; - mParams.identity = identity; - mParams.heap[0] = heap0; - mParams.heap[1] = heap1; - } - virtual ~Surface() { - // TODO: We now have a point here were we can clean-up the - // client's mess. - // This is also where surface id should be recycled. - //LOGD("Surface %d, heaps={%p, %p} destroyed", - // mId, mHeap[0].get(), mHeap[1].get()); - } - - virtual void getSurfaceData( - ISurfaceFlingerClient::surface_data_t* params) const { - *params = mParams; - } - - virtual status_t registerBuffers(const ISurface::BufferHeap& buffers) - { return INVALID_OPERATION; } - virtual void postBuffer(ssize_t offset) { } - virtual void unregisterBuffers() { }; - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format) { - return NULL; - }; + 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: - ISurfaceFlingerClient::surface_data_t mParams; + 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); + + protected: + friend class LayerBaseClient; + sp<SurfaceFlinger> mFlinger; + int32_t mToken; + int32_t mIdentity; + wp<LayerBaseClient> mOwner; }; -private: - int32_t mIndex; + friend class Surface; +private: + int32_t mIndex; + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + // only read + const uint32_t mIdentity; + static int32_t sIdentity; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp deleted file mode 100644 index 397ddc8..0000000 --- a/libs/surfaceflinger/LayerBitmap.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 "SurfaceFlinger" - -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> - -#include <cutils/memory.h> -#include <utils/Errors.h> -#include <utils/Log.h> -#include <utils/MemoryDealer.h> -#include <utils/IMemory.h> -#include <ui/PixelFormat.h> -#include <pixelflinger/pixelflinger.h> - -#include "LayerBitmap.h" -#include "SurfaceFlinger.h" -#include "VRamHeap.h" - - -namespace android { - -// --------------------------------------------------------------------------- - -LayerBitmap::LayerBitmap() - : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2) -{ - memset(&mSurface, 0, sizeof(mSurface)); -} - -LayerBitmap::~LayerBitmap() -{ - mSurface.data = 0; -} - -status_t LayerBitmap::init(const sp<MemoryDealer>& allocator) -{ - if (mAllocator != NULL) - return BAD_VALUE; - mAllocator = allocator; - return NO_ERROR; -} - -status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, - PixelFormat format, uint32_t flags) -{ - const sp<MemoryDealer>& allocator(mAllocator); - if (allocator == NULL) - return NO_INIT; - - if (UNLIKELY(w == mSurface.width && h == mSurface.height && - format == mSurface.format)) - { // same format and size, do nothing. - return NO_ERROR; - } - - PixelFormatInfo info; - getPixelFormatInfo(format, &info); - - uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED; - const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT - const uint32_t Bpp = info.bytesPerPixel; - uint32_t stride = (w + (alignment-1)) & ~(alignment-1); - stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp; - size_t size = info.getScanlineSize(stride) * h; - if (allocFlags & MemoryDealer::PAGE_ALIGNED) { - size_t pagesize = getpagesize(); - size = (size + (pagesize-1)) & ~(pagesize-1); - } - - /* FIXME: we should be able to have a h/v stride because the user of the - * surface might have stride limitation (for instance h/w codecs often do) - */ - int32_t vstride = 0; - - mAlignment = alignment; - mAllocFlags = allocFlags; - mOffset = 0; - if (mSize != size) { - // would be nice to have a reallocate() api - mBitsMemory.clear(); // free-memory - mBitsMemory = allocator->allocate(size, allocFlags); - mSize = size; - } else { - // don't erase memory if we didn't have to reallocate - flags &= ~SECURE_BITS; - } - if (mBitsMemory != 0) { - mOffset = mBitsMemory->offset(); - mSurface.data = static_cast<GGLubyte*>(mBitsMemory->pointer()); - mSurface.version = sizeof(GGLSurface); - mSurface.width = w; - mSurface.height = h; - mSurface.stride = stride; - mSurface.vstride = vstride; - mSurface.format = format; - if (flags & SECURE_BITS) - clear(); - } - - if (mBitsMemory==0 || mSurface.data==0) { - LOGE("not enough memory for layer bitmap " - "size=%u (w=%d, h=%d, stride=%d, format=%d)", - size, int(w), int(h), int(stride), int(format)); - allocator->dump("LayerBitmap"); - mSurface.data = 0; - mSize = -1U; - return NO_MEMORY; - } - return NO_ERROR; -} - -void LayerBitmap::clear() -{ - // NOTE: this memset should not be necessary, at least for - // opaque surface. However, for security reasons it's better to keep it - // (in the case of pmem, it's possible that the memory contains old - // data) - if (mSurface.data) { - memset(mSurface.data, 0, mSize); - //if (bytesPerPixel(mSurface.format) == 4) { - // android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize); - //} else { - // android_memset16((uint16_t*)mSurface.data, 0xF800, mSize); - //} - } -} - -status_t LayerBitmap::getInfo(surface_info_t* info) const -{ - if (mSurface.data == 0) { - memset(info, 0, sizeof(surface_info_t)); - info->bits_offset = NO_MEMORY; - return NO_MEMORY; - } - info->w = uint16_t(width()); - info->h = uint16_t(height()); - info->stride= uint16_t(stride()); - info->bpr = uint16_t(stride() * bytesPerPixel(pixelFormat())); - info->format= uint8_t(pixelFormat()); - info->flags = surface_info_t::eBufferDirty; - info->bits_offset = ssize_t(mOffset); - return NO_ERROR; -} - -status_t LayerBitmap::resize(uint32_t w, uint32_t h) -{ - int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS); - return err; -} - -size_t LayerBitmap::size() const -{ - return mSize; -} - -void LayerBitmap::getBitmapSurface(copybit_image_t* img) const -{ - const sp<IMemoryHeap>& mh(getAllocator()->getMemoryHeap()); - void* sbase = mh->base(); - const GGLSurface& t(surface()); - img->w = t.stride ?: t.width; - img->h = t.vstride ?: t.height; - img->format = t.format; - img->offset = intptr_t(t.data) - intptr_t(sbase); - img->base = sbase; - img->fd = mh->heapID(); -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h deleted file mode 100644 index 9ad64c4..0000000 --- a/libs/surfaceflinger/LayerBitmap.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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_BITMAP_H -#define ANDROID_LAYER_BITMAP_H - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Atomic.h> -#include <ui/PixelFormat.h> -#include <ui/Rect.h> -#include <private/ui/SharedState.h> -#include <pixelflinger/pixelflinger.h> - -class copybit_image_t; - -namespace android { - -// --------------------------------------------------------------------------- - -class IMemory; -class MemoryDealer; -class LayerBitmap; - -// --------------------------------------------------------------------------- - -class LayerBitmap -{ -public: - - enum { - // erase memory to ensure security when necessary - SECURE_BITS = 0x00000001 - }; - - LayerBitmap(); - ~LayerBitmap(); - status_t init(const sp<MemoryDealer>& allocator); - - status_t setBits(uint32_t w, uint32_t h, uint32_t alignment, - PixelFormat format, uint32_t flags = 0); - void clear(); - - status_t getInfo(surface_info_t* info) const; - status_t resize(uint32_t w, uint32_t h); - - const GGLSurface& surface() const { return mSurface; } - Rect bounds() const { return Rect(width(), height()); } - uint32_t width() const { return surface().width; } - uint32_t height() const { return surface().height; } - uint32_t stride() const { return surface().stride; } - PixelFormat pixelFormat() const { return surface().format; } - void* serverBits() const { return surface().data; } - size_t size() const; - const sp<MemoryDealer>& getAllocator() const { return mAllocator; } - void getBitmapSurface(copybit_image_t* img) const; - -private: - sp<MemoryDealer> mAllocator; - sp<IMemory> mBitsMemory; - uint32_t mAllocFlags; - ssize_t mOffset; - GGLSurface mSurface; - size_t mSize; - uint32_t mAlignment; -}; - -}; // namespace android - -#endif // ANDROID_LAYER_BITMAP_H diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index d3e456f..5fd7904 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> @@ -26,6 +24,7 @@ #include <GLES/gl.h> #include <GLES/glext.h> +#include "clz.h" #include "BlurFilter.h" #include "LayerBlur.h" #include "SurfaceFlinger.h" @@ -40,17 +39,18 @@ const char* const LayerBlur::typeID = "LayerBlur"; // --------------------------------------------------------------------------- LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i) - : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), - mRefreshCache(true), mCacheAge(0), mTextureName(-1U) + 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); - deletedTextures.add(mTextureName); + glDeleteTextures(1, &mTextureName); } } @@ -137,44 +137,73 @@ void LayerBlur::onDraw(const Region& clip) const // 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::iterator iterator(clip); - if (iterator) { + 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; - - // allocate enough memory for 4-bytes (2 pixels) aligned data - const int32_t s = (w + 1) & ~1; - uint16_t* const pixels = (uint16_t*)malloc(s*h*2); + + 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, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); - + 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 = GGL_PIXEL_FORMAT_RGB_565; + bl.format = mBlurFormat; bl.data = (GGLubyte*)pixels; blurFilter(&bl, 8, 2); - - // NOTE: this works only because we have POT. we'd have to round the - // texture size up, otherwise. - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + + 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; @@ -186,7 +215,12 @@ void LayerBlur::onDraw(const Region& clip) const glDisable(GL_BLEND); } - glDisable(GL_DITHER); + 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); @@ -196,37 +230,34 @@ void LayerBlur::onDraw(const Region& clip) const // This is a very rare scenario. glMatrixMode(GL_TEXTURE); glLoadIdentity(); - glScalef(1.0f/w, -1.0f/h, 1); - glTranslatef(-x, -y, 0); + 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); - Rect r; - while (iterator.iterate(&r)) { + 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 { - Region::iterator iterator(clip); - if (iterator) { - // 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 (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, w, h); - } + // 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); } } } - - glDisableClientState(GL_TEXTURE_COORD_ARRAY); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h index 24b1156..2e9d7c6 100644 --- a/libs/surfaceflinger/LayerBlur.h +++ b/libs/surfaceflinger/LayerBlur.h @@ -39,7 +39,7 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerBlur(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBlur(); virtual void onDraw(const Region& clip) const; @@ -56,6 +56,12 @@ private: 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; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 00fab70..a36304c 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <math.h> @@ -25,29 +23,28 @@ #include <utils/Log.h> #include <utils/StopWatch.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> - +#include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> -#include <ui/EGLDisplaySurface.h> +#include <ui/FramebufferNativeWindow.h> + +#include <hardware/copybit.h> #include "LayerBuffer.h" #include "SurfaceFlinger.h" -#include "VRamHeap.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, - Client* client, int32_t i) + const sp<Client>& client, int32_t i) : LayerBaseClient(flinger, display, client, i), mNeedsBlending(false) { @@ -55,30 +52,34 @@ LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, LayerBuffer::~LayerBuffer() { - sp<SurfaceBuffer> s(getClientSurface()); - if (s != 0) { - s->disown(); - mClientSurface.clear(); +} + +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; + } } } -sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const +sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const { - Mutex::Autolock _l(mLock); - return mClientSurface.promote(); + return mSurface; } -sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const +status_t LayerBuffer::ditch() { - sp<SurfaceBuffer> s; - Mutex::Autolock _l(mLock); - s = mClientSurface.promote(); - if (s == 0) { - s = new SurfaceBuffer(clientIndex(), - const_cast<LayerBuffer *>(this)); - mClientSurface = s; - } - return s; + mSurface.clear(); + return NO_ERROR; } bool LayerBuffer::needsBlending() const { @@ -140,6 +141,14 @@ bool LayerBuffer::transformed() const return false; } +void LayerBuffer::serverDestroy() +{ + sp<Source> source(clearSource()); + if (source != 0) { + source->destroy(); + } +} + /** * This creates a "buffer" source for this surface */ @@ -189,85 +198,52 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() { } // ============================================================================ -// LayerBuffer::SurfaceBuffer +// LayerBuffer::SurfaceLayerBuffer // ============================================================================ -LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner) -: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner) +LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner) { } -LayerBuffer::SurfaceBuffer::~SurfaceBuffer() +LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer() { unregisterBuffers(); - mOwner = 0; -} - -status_t LayerBuffer::SurfaceBuffer::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch (code) { - case REGISTER_BUFFERS: - case UNREGISTER_BUFFERS: - case CREATE_OVERLAY: - { - // codes that require permission check - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int self_pid = getpid(); - if (LIKELY(pid != self_pid)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - const int uid = ipc->getCallingUid(); - LOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - } - } - } - return LayerBaseClient::Surface::onTransact(code, data, reply, flags); } -status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers( + const ISurface::BufferHeap& buffers) { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) return owner->registerBuffers(buffers); return NO_INIT; } -void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset) +void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset) { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) owner->postBuffer(offset); } -void LayerBuffer::SurfaceBuffer::unregisterBuffers() +void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers() { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) owner->unregisterBuffers(); } -sp<OverlayRef> LayerBuffer::SurfaceBuffer::createOverlay( +sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay( uint32_t w, uint32_t h, int32_t format) { sp<OverlayRef> result; - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) result = owner->createOverlay(w, h, format); return result; } -void LayerBuffer::SurfaceBuffer::disown() -{ - Mutex::Autolock _l(mLock); - mOwner = 0; -} - // ============================================================================ // LayerBuffer::Buffer // ============================================================================ @@ -276,20 +252,36 @@ LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) : mBufferHeap(buffers) { 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.offset = offset; - src.img.base = buffers.heap->base(); - src.img.fd = buffers.heap->heapID(); + 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(), buffers.heap->getSize(), + offset, buffers.heap->base(), + &src.img.handle); + + if (err == NO_ERROR) { + 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); + } + } } LayerBuffer::Buffer::~Buffer() { + NativeBuffer& src(mNativeBuffer); + if (src.img.handle) { + native_handle_delete(src.img.handle); + } } // ============================================================================ @@ -323,8 +315,7 @@ bool LayerBuffer::Source::transformed() const { LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) - : Source(layer), mStatus(NO_ERROR), - mBufferSize(0), mTextureName(-1U) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -363,13 +354,16 @@ LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; mLayer.forceVisibilityTransaction(); - } LayerBuffer::BufferSource::~BufferSource() { - if (mTextureName != -1U) { - LayerBase::deletedTextures.add(mTextureName); + if (mTexture.name != -1U) { + glDeleteTextures(1, &mTexture.name); + } + if (mTexture.image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); } } @@ -377,7 +371,7 @@ void LayerBuffer::BufferSource::postBuffer(ssize_t offset) { ISurface::BufferHeap buffers; { // scope for the lock - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mBufferSourceLock); buffers = mBufferHeap; if (buffers.heap != 0) { const size_t memorySize = buffers.heap->getSize(); @@ -402,7 +396,7 @@ void LayerBuffer::BufferSource::postBuffer(ssize_t offset) void LayerBuffer::BufferSource::unregisterBuffers() { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mBufferSourceLock); mBufferHeap.heap.clear(); mBuffer.clear(); mLayer.invalidate(); @@ -410,13 +404,13 @@ void LayerBuffer::BufferSource::unregisterBuffers() sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mBufferSourceLock); return mBuffer; } void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) { - Mutex::Autolock _l(mLock); + Mutex::Autolock _l(mBufferSourceLock); mBuffer = buffer; } @@ -427,113 +421,40 @@ bool LayerBuffer::BufferSource::transformed() const void LayerBuffer::BufferSource::onDraw(const Region& clip) const { - sp<Buffer> buffer(getBuffer()); - if (UNLIKELY(buffer == 0)) { + 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(buffer->getBuffer()); - const Rect& transformedBounds = mLayer.getTransformedBounds(); - const int can_use_copybit = mLayer.canUseCopybit(); - - if (can_use_copybit) { - const int src_width = src.crop.r - src.crop.l; - const int src_height = src.crop.b - src.crop.t; - int W = transformedBounds.width(); - int H = transformedBounds.height(); - if (mLayer.getOrientation() & Transform::ROT_90) { - int t(W); W=H; H=t; - } + NativeBuffer src(ourBuffer->getBuffer()); + const Rect transformedBounds(mLayer.getTransformedBounds()); - /* With LayerBuffer, it is likely that we'll have to rescale the - * surface, because this is often used for video playback or - * camera-preview. Since we want these operation as fast as possible - * we make sure we can use the 2D H/W even if it doesn't support - * the requested scale factor, in which case we perform the scaling - * in several passes. */ - - copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine(); - const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); - const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); - - float xscale = 1.0f; - if (src_width > W*min) xscale = 1.0f / min; - else if (src_width*mag < W) xscale = mag; - - float yscale = 1.0f; - if (src_height > H*min) yscale = 1.0f / min; - else if (src_height*mag < H) yscale = mag; - - if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) { - if (UNLIKELY(mTemporaryDealer == 0)) { - // allocate a memory-dealer for this the first time - mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager() - ->createHeap(ISurfaceComposer::eHardware); - mTempBitmap.init(mTemporaryDealer); - } + if (UNLIKELY(mTexture.name == -1LU)) { + mTexture.name = mLayer.createTexture(); + } - const int tmp_w = floorf(src_width * xscale); - const int tmp_h = floorf(src_height * yscale); - err = mTempBitmap.setBits(tmp_w, tmp_h, 1, src.img.format); - - if (LIKELY(err == NO_ERROR)) { - NativeBuffer tmp; - mTempBitmap.getBitmapSurface(&tmp.img); - tmp.crop.l = 0; - tmp.crop.t = 0; - tmp.crop.r = tmp.img.w; - tmp.crop.b = tmp.img.h; - - region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it); - src = tmp; - } - } +#if defined(EGL_ANDROID_image_native_buffer) + if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( + src.crop.r, src.crop.b, src.img.format, + GraphicBuffer::USAGE_HW_TEXTURE, + src.img.w, src.img.handle, false); - const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware()); - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(transformedBounds); - const State& s(mLayer.drawingState()); - region_iterator it(clip); - - // pick the right orientation for this buffer - int orientation = mLayer.getOrientation(); - if (UNLIKELY(mBufferHeap.transform)) { - Transform rot90; - GraphicPlane::orientationToTransfrom( - ISurfaceComposer::eOrientation90, 0, 0, &rot90); - const Transform& planeTransform(mLayer.graphicPlane(0).transform()); - const Layer::State& s(mLayer.drawingState()); - Transform tr(planeTransform * s.transform * rot90); - orientation = tr.getOrientation(); - } - - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - - err = copybit->stretch(copybit, - &dst, &src.img, &drect, &src.crop, &it); - if (err != NO_ERROR) { - LOGE("copybit failed (%s)", strerror(err)); - } + graphicBuffer->setVerticalStride(src.img.h); + + err = mLayer.initializeEglImage(graphicBuffer, &mTexture); + } +#endif + else { + err = INVALID_OPERATION; } - if (!can_use_copybit || err) { - if (UNLIKELY(mTextureName == -1LU)) { - mTextureName = mLayer.createTexture(); - } - GLuint w = 0; - GLuint h = 0; + if (err != NO_ERROR) { + // slower fallback GGLSurface t; t.version = sizeof(GGLSurface); t.width = src.crop.r; @@ -541,13 +462,14 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.stride = src.img.w; t.vstride= src.img.h; t.format = src.img.format; - t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset); + t.data = (GGLubyte*)src.img.base; const Region dirty(Rect(t.width, t.height)); - mLayer.loadTexture(dirty, mTextureName, t, w, h); - mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform); + mLayer.loadTexture(&mTexture, dirty, t); } -} + mTexture.transform = mBufferHeap.transform; + mLayer.drawWithOpenGL(clip, mTexture); +} // --------------------------------------------------------------------------- @@ -580,15 +502,15 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, mFormat = overlay->format; mWidthStride = overlay->w_stride; mHeightStride = overlay->h_stride; + mInitialized = false; mOverlayHandle = overlay->getHandleRef(overlay); - // NOTE: here it's okay to acquire a reference to "this"m as long as - // the reference is not released before we leave the ctor. - sp<OverlayChannel> channel = new OverlayChannel(this); + sp<OverlayChannel> channel = new OverlayChannel( &layer ); *overlayRef = new OverlayRef(mOverlayHandle, channel, mWidth, mHeight, mFormat, mWidthStride, mHeightStride); + mLayer.mFlinger->signalEvent(); } LayerBuffer::OverlaySource::~OverlaySource() @@ -599,6 +521,15 @@ LayerBuffer::OverlaySource::~OverlaySource() } } +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()); @@ -614,37 +545,33 @@ void LayerBuffer::OverlaySource::onVisibilityResolved( // this code-path must be as tight as possible, it's called each time // the screen is composited. if (UNLIKELY(mOverlay != 0)) { - if (mVisibilityChanged) { + if (mVisibilityChanged || !mInitialized) { mVisibilityChanged = false; - const Rect& bounds = mLayer.getTransformedBounds(); + 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(mLock); + Mutex::Autolock _l(mOverlaySourceLock); if (mOverlay) { overlay_control_device_t* overlay_dev = mOverlayDevice; overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); - overlay_dev->setParameter(overlay_dev, mOverlay, + overlay_dev->setParameter(overlay_dev, mOverlay, OVERLAY_TRANSFORM, mLayer.getOrientation()); + overlay_dev->commit(overlay_dev, mOverlay); } } } } -void LayerBuffer::OverlaySource::serverDestroy() -{ - mLayer.clearSource(); - destroyOverlay(); -} - -void LayerBuffer::OverlaySource::destroyOverlay() +void LayerBuffer::OverlaySource::destroy() { // we need a lock here to protect "onVisibilityResolved" - Mutex::Autolock _l(mLock); - if (mOverlay) { + Mutex::Autolock _l(mOverlaySourceLock); + if (mOverlay && mOverlayDevice) { overlay_control_device_t* overlay_dev = mOverlayDevice; overlay_dev->destroyOverlay(overlay_dev, mOverlay); mOverlay = 0; diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 2dc77f1..47482f4 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -20,21 +20,20 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/IMemory.h> -#include <private/ui/LayerState.h> -#include <EGL/eglnatives.h> - #include "LayerBase.h" -#include "LayerBitmap.h" + +struct copybit_device_t; namespace android { // --------------------------------------------------------------------------- -class MemoryDealer; +class Buffer; class Region; class OverlayRef; +// --------------------------------------------------------------------------- + class LayerBuffer : public LayerBaseClient { class Source : public LightRefBase<Source> { @@ -47,11 +46,11 @@ class LayerBuffer : public LayerBaseClient 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; @@ -59,12 +58,14 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerBuffer(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBuffer(); + virtual void onFirstRef(); virtual bool needsBlending() const; - virtual sp<LayerBaseClient::Surface> getSurface() 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); @@ -78,16 +79,23 @@ public: sp<Source> getSource() const; sp<Source> clearSource(); void setNeedsBlending(bool blending); - const Rect& getTransformedBounds() const { + 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); @@ -120,15 +128,15 @@ private: virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); virtual bool transformed() const; + virtual void destroy() { } private: - mutable Mutex mLock; - sp<Buffer> mBuffer; - status_t mStatus; - ISurface::BufferHeap mBufferHeap; - size_t mBufferSize; - mutable sp<MemoryDealer> mTemporaryDealer; - mutable LayerBitmap mTempBitmap; - mutable GLuint mTextureName; + mutable Mutex mBufferSourceLock; + sp<Buffer> mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable sp<GraphicBuffer> mTempBitmap; + mutable LayerBase::Texture mTexture; }; class OverlaySource : public Source { @@ -137,30 +145,26 @@ private: sp<OverlayRef>* overlayRef, uint32_t w, uint32_t h, int32_t format); 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: - void serverDestroy(); - void destroyOverlay(); + class OverlayChannel : public BnOverlay { - mutable Mutex mLock; - sp<OverlaySource> mSource; + wp<LayerBuffer> mLayer; virtual void destroy() { - sp<OverlaySource> source; - { // scope for the lock; - Mutex::Autolock _l(mLock); - source = mSource; - mSource.clear(); - } - if (source != 0) { - source->serverDestroy(); + sp<LayerBuffer> layer(mLayer.promote()); + if (layer != 0) { + layer->serverDestroy(); } } public: - OverlayChannel(const sp<OverlaySource>& source) - : mSource(source) { + OverlayChannel(const sp<LayerBuffer>& layer) + : mLayer(layer) { } }; + friend class OverlayChannel; bool mVisibilityChanged; @@ -172,41 +176,35 @@ private: int32_t mFormat; int32_t mWidthStride; int32_t mHeightStride; - mutable Mutex mLock; + mutable Mutex mOverlaySourceLock; + bool mInitialized; }; - class SurfaceBuffer : public LayerBaseClient::Surface + class SurfaceLayerBuffer : public LayerBaseClient::Surface { public: - SurfaceBuffer(SurfaceID id, LayerBuffer* owner); - virtual ~SurfaceBuffer(); - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + 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); - void disown(); private: - LayerBuffer* getOwner() const { - Mutex::Autolock _l(mLock); - return mOwner; + sp<LayerBuffer> getOwner() const { + return static_cast<LayerBuffer*>(Surface::getOwner().get()); } - mutable Mutex mLock; - LayerBuffer* mOwner; }; - friend class SurfaceFlinger; - sp<SurfaceBuffer> getClientSurface() const; - mutable Mutex mLock; sp<Source> mSource; - + sp<Surface> mSurface; bool mInvalidate; bool mNeedsBlending; - mutable wp<SurfaceBuffer> mClientSurface; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index 0c347cc..fd61e30 100644 --- a/libs/surfaceflinger/LayerDim.cpp +++ b/libs/surfaceflinger/LayerDim.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> @@ -23,9 +21,10 @@ #include <utils/Errors.h> #include <utils/Log.h> +#include <ui/GraphicBuffer.h> + #include "LayerDim.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" namespace android { @@ -33,27 +32,76 @@ namespace android { const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; const char* const LayerDim::typeID = "LayerDim"; -sp<MemoryDealer> LayerDim::mDimmerDealer; -LayerBitmap LayerDim::mDimmerBitmap; + +bool LayerDim::sUseTexture; +GLuint LayerDim::sTexId; +EGLImageKHR LayerDim::sImage; +int32_t LayerDim::sWidth; +int32_t LayerDim::sHeight; // --------------------------------------------------------------------------- LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i) - : LayerBaseClient(flinger, display, client, i) + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i) { } void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) { - // must only be called once. - mDimmerDealer = flinger->getSurfaceHeapManager() - ->createHeap(ISurfaceComposer::eHardware); - if (mDimmerDealer != 0) { - mDimmerBitmap.init(mDimmerDealer); - mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565); - mDimmerBitmap.clear(); + 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() @@ -63,49 +111,56 @@ LayerDim::~LayerDim() void LayerDim::onDraw(const Region& clip) const { const State& s(drawingState()); - - Region::iterator iterator(clip); - if (s.alpha>0 && iterator) { + 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()); - - status_t err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - // StopWatch watch("copybit"); - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); - - copybit_image_t src; - mDimmerBitmap.getBitmapSurface(&src); - const copybit_rect_t& srect(drect); - - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - region_iterator it(clip); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + 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); } - if (!can_use_copybit || err) { - const GGLfixed alpha = (s.alpha << 16)/255; - const uint32_t fbHeight = hw.getHeight(); - glDisable(GL_TEXTURE_2D); - glDisable(GL_DITHER); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0, 0, 0, alpha); - glVertexPointer(2, GL_FIXED, 0, mVertices); - Rect r; - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + 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); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h index 3e37a47..d4672a1 100644 --- a/libs/surfaceflinger/LayerDim.h +++ b/libs/surfaceflinger/LayerDim.h @@ -20,15 +20,22 @@ #include <stdint.h> #include <sys/types.h> -#include "LayerBase.h" -#include "LayerBitmap.h" +#include <EGL/egl.h> +#include <EGL/eglext.h> -namespace android { +#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; @@ -36,7 +43,7 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerDim(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; @@ -44,10 +51,6 @@ public: virtual bool isSecure() const { return false; } static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); - -private: - static sp<MemoryDealer> mDimmerDealer; - static LayerBitmap mDimmerBitmap; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp deleted file mode 100644 index 79e5328..0000000 --- a/libs/surfaceflinger/LayerOrientationAnim.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 "SurfaceFlinger" - -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/Log.h> -#include <utils/StopWatch.h> - -#include <core/SkBitmap.h> - -#include <ui/EGLDisplaySurface.h> - -#include "BlurFilter.h" -#include "LayerBase.h" -#include "LayerOrientationAnim.h" -#include "SurfaceFlinger.h" -#include "DisplayHardware/DisplayHardware.h" -#include "OrientationAnimation.h" - -namespace android { -// --------------------------------------------------------------------------- - -const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; -const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; - -// --------------------------------------------------------------------------- - -// Animation... -const float DURATION = ms2ns(200); -const float BOUNCES_PER_SECOND = 0.5f; -const float DIM_TARGET = 0.40f; -#define INTERPOLATED_TIME(_t) (_t) - -// --------------------------------------------------------------------------- - -LayerOrientationAnim::LayerOrientationAnim( - SurfaceFlinger* flinger, DisplayID display, - OrientationAnimation* anim, - const LayerBitmap& bitmapIn, - const LayerBitmap& bitmapOut) - : LayerOrientationAnimBase(flinger, display), mAnim(anim), - mBitmapIn(bitmapIn), mBitmapOut(bitmapOut), - mTextureName(-1), mTextureNameIn(-1) -{ - // blur that texture. - mOrientationCompleted = false; - mNeedsBlending = false; -} - -LayerOrientationAnim::~LayerOrientationAnim() -{ - if (mTextureName != -1U) { - LayerBase::deletedTextures.add(mTextureName); - } - if (mTextureNameIn != -1U) { - LayerBase::deletedTextures.add(mTextureNameIn); - } -} - -bool LayerOrientationAnim::needsBlending() const -{ - return mNeedsBlending; -} - -Point LayerOrientationAnim::getPhysicalSize() const -{ - const GraphicPlane& plane(graphicPlane(0)); - const DisplayHardware& hw(plane.displayHardware()); - return Point(hw.getWidth(), hw.getHeight()); -} - -void LayerOrientationAnim::validateVisibility(const Transform&) -{ - const Layer::State& s(drawingState()); - const Transform tr(s.transform); - const Point size(getPhysicalSize()); - uint32_t w = size.x; - uint32_t h = size.y; - mTransformedBounds = tr.makeBounds(w, h); - mLeft = tr.tx(); - mTop = tr.ty(); - transparentRegionScreen.clear(); - mTransformed = true; - mCanUseCopyBit = false; - copybit_device_t* copybit = mFlinger->getBlitEngine(); - if (copybit) { - mCanUseCopyBit = true; - } -} - -void LayerOrientationAnim::onOrientationCompleted() -{ - mAnim->onAnimationFinished(); -} - -void LayerOrientationAnim::onDraw(const Region& clip) const -{ - float alphaIn = DIM_TARGET; - - // clear screen - // TODO: with update on demand, we may be able - // to not erase the screen at all during the animation - if (!mOrientationCompleted) { - glDisable(GL_BLEND); - glDisable(GL_DITHER); - glDisable(GL_SCISSOR_TEST); - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT); - } - - copybit_image_t dst; - const GraphicPlane& plane(graphicPlane(0)); - const DisplayHardware& hw(plane.displayHardware()); - hw.getDisplaySurface(&dst); - - copybit_image_t src; - mBitmapIn.getBitmapSurface(&src); - - copybit_image_t srcOut; - mBitmapOut.getBitmapSurface(&srcOut); - - const int w = dst.w; - const int h = dst.h; - const int xc = uint32_t(dst.w-w)/2; - const int yc = uint32_t(dst.h-h)/2; - const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; - const copybit_rect_t srect = { 0, 0, src.w, src.h }; - const Region reg(Rect( drect.l, drect.t, drect.r, drect.b )); - - int err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - - if (alphaIn > 0) { - region_iterator it(reg); - copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_ENABLE); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alphaIn*255)); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_DISABLE); - } - LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err)); - } - if (!can_use_copybit || err) { - GGLSurface t; - t.version = sizeof(GGLSurface); - t.width = src.w; - t.height = src.h; - t.stride = src.w; - t.vstride= src.h; - t.format = src.format; - t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); - - Transform tr; - tr.set(xc, yc); - - // FIXME: we should not access mVertices and mDrawingState like that, - // but since we control the animation, we know it's going to work okay. - // eventually we'd need a more formal way of doing things like this. - LayerOrientationAnim& self(const_cast<LayerOrientationAnim&>(*this)); - tr.transform(self.mVertices[0], 0, 0); - tr.transform(self.mVertices[1], 0, src.h); - tr.transform(self.mVertices[2], src.w, src.h); - tr.transform(self.mVertices[3], src.w, 0); - if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { - // Too slow to do this in software - self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; - } - - if (alphaIn > 0.0f) { - t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); - if (UNLIKELY(mTextureNameIn == -1LU)) { - mTextureNameIn = createTexture(); - GLuint w=0, h=0; - const Region dirty(Rect(t.width, t.height)); - loadTexture(dirty, mTextureNameIn, t, w, h); - } - self.mDrawingState.alpha = int(alphaIn*255); - drawWithOpenGL(reg, mTextureNameIn, t); - } - } -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h deleted file mode 100644 index 12b6f1c..0000000 --- a/libs/surfaceflinger/LayerOrientationAnim.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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_ORIENTATION_ANIM_H -#define ANDROID_LAYER_ORIENTATION_ANIM_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/threads.h> -#include <utils/Parcel.h> - -#include "LayerBase.h" -#include "LayerBitmap.h" - -namespace android { - -// --------------------------------------------------------------------------- -class OrientationAnimation; - - -class LayerOrientationAnimBase : public LayerBase -{ -public: - LayerOrientationAnimBase(SurfaceFlinger* flinger, DisplayID display) - : LayerBase(flinger, display) { - } - virtual void onOrientationCompleted() = 0; -}; - -// --------------------------------------------------------------------------- - -class LayerOrientationAnim : public LayerOrientationAnimBase -{ -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; } - - LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, - OrientationAnimation* anim, - const LayerBitmap& bitmapIn, - const LayerBitmap& bitmapOut); - virtual ~LayerOrientationAnim(); - - void onOrientationCompleted(); - - virtual void onDraw(const Region& clip) const; - virtual Point getPhysicalSize() const; - virtual void validateVisibility(const Transform& globalTransform); - virtual bool needsBlending() const; - virtual bool isSecure() const { return false; } -private: - OrientationAnimation* mAnim; - LayerBitmap mBitmapIn; - LayerBitmap mBitmapOut; - bool mOrientationCompleted; - mutable GLuint mTextureName; - mutable GLuint mTextureNameIn; - mutable bool mNeedsBlending; -}; - -// --------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_LAYER_ORIENTATION_ANIM_H diff --git a/libs/surfaceflinger/MessageQueue.cpp b/libs/surfaceflinger/MessageQueue.cpp new file mode 100644 index 0000000..b43d801 --- /dev/null +++ b/libs/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/libs/surfaceflinger/MessageQueue.h b/libs/surfaceflinger/MessageQueue.h new file mode 100644 index 0000000..dc8138d --- /dev/null +++ b/libs/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/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/OrientationAnimation.cpp deleted file mode 100644 index 12c0eef..0000000 --- a/libs/surfaceflinger/OrientationAnimation.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 "SurfaceFlinger" - -#include <stdint.h> -#include <sys/types.h> -#include <limits.h> - -#include "LayerOrientationAnim.h" -#include "OrientationAnimation.h" -#include "SurfaceFlinger.h" -#include "VRamHeap.h" - -#include "DisplayHardware/DisplayHardware.h" - -namespace android { - -// --------------------------------------------------------------------------- - -OrientationAnimation::OrientationAnimation(const sp<SurfaceFlinger>& flinger) - : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE) -{ - // allocate a memory-dealer for this the first time - mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap( - ISurfaceComposer::eHardware); -} - -OrientationAnimation::~OrientationAnimation() -{ -} - -void OrientationAnimation::onOrientationChanged(uint32_t type) -{ - if (mState == DONE) { - mType = type; - if (!(type & ISurfaceComposer::eOrientationAnimationDisable)) { - mState = PREPARE; - } - } -} - -void OrientationAnimation::onAnimationFinished() -{ - if (mState != DONE) - mState = FINISH; -} - -bool OrientationAnimation::run_impl() -{ - bool skip_frame; - switch (mState) { - default: - case DONE: - skip_frame = done(); - break; - case PREPARE: - skip_frame = prepare(); - break; - case PHASE1: - skip_frame = phase1(); - break; - case PHASE2: - skip_frame = phase2(); - break; - case FINISH: - skip_frame = finished(); - break; - } - return skip_frame; -} - -bool OrientationAnimation::done() -{ - return done_impl(); -} - -bool OrientationAnimation::prepare() -{ - mState = PHASE1; - - const GraphicPlane& plane(mFlinger->graphicPlane(0)); - const DisplayHardware& hw(plane.displayHardware()); - const uint32_t w = hw.getWidth(); - const uint32_t h = hw.getHeight(); - - LayerBitmap bitmap; - bitmap.init(mTemporaryDealer); - bitmap.setBits(w, h, 1, hw.getFormat()); - - LayerBitmap bitmapIn; - bitmapIn.init(mTemporaryDealer); - bitmapIn.setBits(w, h, 1, hw.getFormat()); - - copybit_image_t front; - bitmap.getBitmapSurface(&front); - hw.copyFrontToImage(front); - - LayerOrientationAnimBase* l; - - l = new LayerOrientationAnim( - mFlinger.get(), 0, this, bitmap, bitmapIn); - - l->initStates(w, h, 0); - l->setLayer(INT_MAX-1); - mFlinger->addLayer(l); - mLayerOrientationAnim = l; - return true; -} - -bool OrientationAnimation::phase1() -{ - if (mFlinger->isFrozen() == false) { - // start phase 2 - mState = PHASE2; - mLayerOrientationAnim->onOrientationCompleted(); - mLayerOrientationAnim->invalidate(); - return true; - - } - //mLayerOrientationAnim->invalidate(); - return false; -} - -bool OrientationAnimation::phase2() -{ - // do the 2nd phase of the animation - mLayerOrientationAnim->invalidate(); - return false; -} - -bool OrientationAnimation::finished() -{ - mState = DONE; - mFlinger->removeLayer(mLayerOrientationAnim); - mLayerOrientationAnim = NULL; - return true; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/OrientationAnimation.h deleted file mode 100644 index cafa38d..0000000 --- a/libs/surfaceflinger/OrientationAnimation.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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_ORIENTATION_ANIMATION_H -#define ANDROID_ORIENTATION_ANIMATION_H - -#include <stdint.h> -#include <sys/types.h> - -#include "SurfaceFlinger.h" - -namespace android { - -// --------------------------------------------------------------------------- - -class SurfaceFlinger; -class MemoryDealer; -class LayerOrientationAnim; - -class OrientationAnimation -{ -public: - OrientationAnimation(const sp<SurfaceFlinger>& flinger); - virtual ~OrientationAnimation(); - - void onOrientationChanged(uint32_t type); - void onAnimationFinished(); - inline bool run() { - if (LIKELY(mState == DONE)) - return done_impl(); - return run_impl(); - } - -private: - enum { - DONE = 0, - PREPARE, - PHASE1, - PHASE2, - FINISH - }; - - bool run_impl(); - inline bool done_impl() { - if (mFlinger->isFrozen()) { - // we are not allowed to draw, but pause a bit to make sure - // apps don't end up using the whole CPU, if they depend on - // surfaceflinger for synchronization. - usleep(8333); // 8.3ms ~ 120fps - return true; - } - return false; - } - - bool done(); - bool prepare(); - bool phase1(); - bool phase2(); - bool finished(); - - sp<SurfaceFlinger> mFlinger; - sp<MemoryDealer> mTemporaryDealer; - LayerOrientationAnimBase* mLayerOrientationAnim; - int mState; - uint32_t mType; -}; - -// --------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_ORIENTATION_ANIMATION_H diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 97dfecc..9694cf1 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdio.h> #include <stdint.h> @@ -23,6 +21,7 @@ #include <fcntl.h> #include <errno.h> #include <math.h> +#include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -30,36 +29,29 @@ #include <cutils/log.h> #include <cutils/properties.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> -#include <utils/MemoryDealer.h> -#include <utils/MemoryBase.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 <ui/DisplayInfo.h> -#include <ui/EGLDisplaySurface.h> #include <pixelflinger/pixelflinger.h> #include <GLES/gl.h> #include "clz.h" -#include "CPUGauge.h" #include "Layer.h" #include "LayerBlur.h" #include "LayerBuffer.h" #include "LayerDim.h" -#include "LayerBitmap.h" -#include "LayerOrientationAnim.h" -#include "OrientationAnimation.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" -#include "GPUHardware/GPUHardware.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 @@ -93,30 +85,30 @@ SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) } ssize_t SurfaceFlinger::LayerVector::indexOf( - LayerBase* key, size_t guess) const + 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); - LOG_ASSERT(layers[idx]==key, + LOGE_IF(layers[idx]!=key, "LayerVector[%p]: layers[%d]=%p, key=%p", - this, int(idx), layers[idx], key); + this, int(idx), layers[idx].get(), key.get()); return idx; } return i; } ssize_t SurfaceFlinger::LayerVector::add( - LayerBase* layer, - Vector<LayerBase*>::compar_t cmp) + 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; - LayerBase* const* a = layers.array(); + sp<LayerBase> const* a = layers.array(); while (l <= h) { mid = l + (h - l)/2; const int c = cmp(a+mid, &layer); @@ -139,14 +131,14 @@ ssize_t SurfaceFlinger::LayerVector::add( return order; } -ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) +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); - LOG_ASSERT(layers[index]==layer, + LOGE_IF(layers[index]!=layer, "LayerVector[%p]: layers[%u]=%p, layer=%p", - this, int(index), layers[index], layer); + this, int(index), layers[index].get(), layer.get()); layers.removeItemsAt(index); lookup.removeItemsAt(keyIndex); const size_t count = lookup.size(); @@ -161,8 +153,8 @@ ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) } ssize_t SurfaceFlinger::LayerVector::reorder( - LayerBase* layer, - Vector<LayerBase*>::compar_t cmp) + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) { // XXX: it's a little lame. but oh well... ssize_t err = remove(layer); @@ -180,19 +172,24 @@ SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), mTransactionCount(0), + mResizeTransationPending(false), + mLayersRemoved(false), mBootTime(systemTime()), - mLastScheduledBroadcast(NULL), + 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), - mDebugCpu(0), - mDebugFps(0), mDebugBackground(0), - mSyncObject(), - mDeplayedTransactionPending(0), + mDebugInSwapBuffers(0), + mLastSwapBufferTime(0), + mDebugInTransaction(0), + mLastTransactionTime(0), + mBootFinished(false), mConsoleSignals(0), mSecureFrameBuffer(0) { @@ -207,28 +204,16 @@ void SurfaceFlinger::init() char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); - property_get("debug.sf.showcpu", value, "0"); - mDebugCpu = atoi(value); property_get("debug.sf.showbackground", value, "0"); mDebugBackground = atoi(value); - property_get("debug.sf.showfps", value, "0"); - mDebugFps = atoi(value); LOGI_IF(mDebugRegion, "showupdates enabled"); - LOGI_IF(mDebugCpu, "showcpu enabled"); LOGI_IF(mDebugBackground, "showbackground enabled"); - LOGI_IF(mDebugFps, "showfps enabled"); } SurfaceFlinger::~SurfaceFlinger() { glDeleteTextures(1, &mWormholeTexName); - delete mOrientationAnimation; -} - -copybit_device_t* SurfaceFlinger::getBlitEngine() const -{ - return graphicPlane(0).displayHardware().getBlitEngine(); } overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const @@ -236,29 +221,9 @@ overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const return graphicPlane(0).displayHardware().getOverlayEngine(); } -sp<IMemory> SurfaceFlinger::getCblk() const -{ - return mServerCblkMemory; -} - -status_t SurfaceFlinger::requestGPU(const sp<IGPUCallback>& callback, - gpu_info_t* gpu) -{ - if (mGPU == 0) - return INVALID_OPERATION; - - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - status_t err = mGPU->request(pid, callback, gpu); - return err; -} - -status_t SurfaceFlinger::revokeGPU() +sp<IMemoryHeap> SurfaceFlinger::getCblk() const { - if (mGPU == 0) - return INVALID_OPERATION; - - return mGPU->friendlyRevoke(); + return mServerHeap; } sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() @@ -266,33 +231,34 @@ sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() Mutex::Autolock _l(mStateLock); uint32_t token = mTokens.acquire(); - Client* client = new Client(token, this); - if ((client == 0) || (client->ctrlblk == 0)) { + 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) { - delete client; mTokens.release(token); return 0; } sp<BClient> bclient = - new BClient(this, token, client->controlBlockMemory()); + new BClient(this, token, client->getControlBlockMemory()); return bclient; } void SurfaceFlinger::destroyConnection(ClientID cid) { Mutex::Autolock _l(mStateLock); - Client* const client = mClientsMap.valueFor(cid); - if (client) { + sp<Client> client = mClientsMap.valueFor(cid); + if (client != 0) { // free all the layers this client owns - const Vector<LayerBaseClient*>& layers = client->getLayers(); + Vector< wp<LayerBaseClient> > layers(client->getLayers()); const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - LayerBaseClient* const layer = layers[i]; - removeLayer_l(layer); + sp<LayerBaseClient> layer(layers[i].promote()); + if (layer != 0) { + purgatorizeLayer_l(layer); + } } // the resources associated with this client will be freed @@ -328,6 +294,7 @@ 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"); } @@ -339,41 +306,15 @@ void SurfaceFlinger::onFirstRef() mReadyToRunBarrier.wait(); } - static inline uint16_t pack565(int r, int g, int b) { return (r<<11)|(g<<5)|b; } -// this is defined in libGLES_CM.so -extern ISurfaceComposer* GLES_localSurfaceManager; - status_t SurfaceFlinger::readyToRun() { LOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - // create the shared control-block - mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); - LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); - - mServerCblkMemory = mServerHeap->allocate(4096); - LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); - - mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer()); - LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); - new(mServerCblk) surface_flinger_cblk_t; - - // get a reference to the GPU if we have one - mGPU = GPUFactory::getGPU(); - - // create the surface Heap manager, which manages the heaps - // (be it in RAM or VRAM) where surfaces are allocated - // We give 8 MB per client. - mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); - - - GLES_localSurfaceManager = static_cast<ISurfaceComposer*>(this); - // we only support one display currently int dpy = 0; @@ -384,6 +325,16 @@ status_t SurfaceFlinger::readyToRun() 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 @@ -450,13 +401,6 @@ status_t SurfaceFlinger::readyToRun() * We're now ready to accept clients... */ - mOrientationAnimation = new OrientationAnimation(this); - - // start CPU gauge display - if (mDebugCpu) - mCpuGauge = new CPUGauge(this, ms2ns(500)); - - // start boot animation property_set("ctl.start", "bootanim"); @@ -471,45 +415,53 @@ status_t SurfaceFlinger::readyToRun() void SurfaceFlinger::waitForEvent() { - // wait for something to do - if (UNLIKELY(isFrozen())) { - // wait 5 seconds - const nsecs_t freezeDisplayTimeout = ms2ns(5000); - const nsecs_t now = systemTime(); - if (mFreezeDisplayTime == 0) { - mFreezeDisplayTime = now; + while (true) { + nsecs_t timeout = -1; + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + const nsecs_t freezeDisplayTimeout = ms2ns(5000); + const nsecs_t now = systemTime(); + if (mFreezeDisplayTime == 0) { + mFreezeDisplayTime = now; + } + nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); + timeout = waitTime>0 ? waitTime : 0; } - nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); - int err = (waitTime > 0) ? mSyncObject.wait(waitTime) : TIMED_OUT; - if (err != NO_ERROR) { + + MessageList::value_type msg = mEventQueue.waitMessage(timeout); + if (msg != 0) { + mFreezeDisplayTime = 0; + switch (msg->what) { + case MessageQueue::INVALIDATE: + // invalidate message, just return to the main loop + return; + } + } else { + // we timed out if (isFrozen()) { // we timed out and are still frozen LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", mFreezeDisplay, mFreezeCount); mFreezeCount = 0; mFreezeDisplay = false; + return; } } - } else { - mFreezeDisplayTime = 0; - mSyncObject.wait(); } } void SurfaceFlinger::signalEvent() { - mSyncObject.open(); + mEventQueue.invalidate(); } void SurfaceFlinger::signal() const { - mSyncObject.open(); + // this is the IPC call + const_cast<SurfaceFlinger*>(this)->signalEvent(); } void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) { - if (android_atomic_or(1, &mDeplayedTransactionPending) == 0) { - sp<DelayedTransaction> delayedEvent(new DelayedTransaction(this, delay)); - delayedEvent->run("DelayedeEvent", PRIORITY_URGENT_DISPLAY); - } + mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); } // ---------------------------------------------------------------------------- @@ -540,24 +492,20 @@ bool SurfaceFlinger::threadLoop() handlePageFlip(); const DisplayHardware& hw(graphicPlane(0).displayHardware()); - if (LIKELY(hw.canDraw())) { + 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(); - executeScheduledBroadcasts(); - - // sample the cpu gauge - if (UNLIKELY(mDebugCpu)) { - handleDebugCpu(); - } postFramebuffer(); } else { // pretend we did the post unlockClients(); - executeScheduledBroadcasts(); usleep(16667); // 60 fps period } return true; @@ -565,28 +513,14 @@ bool SurfaceFlinger::threadLoop() void SurfaceFlinger::postFramebuffer() { - const bool skip = mOrientationAnimation->run(); - if (UNLIKELY(skip)) { - return; - } - if (!mInvalidRegion.isEmpty()) { const DisplayHardware& hw(graphicPlane(0).displayHardware()); - - if (UNLIKELY(mDebugFps)) { - debugShowFPS(); - } - + const nsecs_t now = systemTime(); + mDebugInSwapBuffers = now; hw.flip(mInvalidRegion); - + mLastSwapBufferTime = systemTime() - now; + mDebugInSwapBuffers = 0; mInvalidRegion.clear(); - - if (Layer::deletedTextures.size()) { - glDeleteTextures( - Layer::deletedTextures.size(), - Layer::deletedTextures.array()); - Layer::deletedTextures.clear(); - } } } @@ -601,15 +535,13 @@ void SurfaceFlinger::handleConsoleEvents() } if (mDeferReleaseConsole && hw.canDraw()) { - // We got the release signal before the aquire signal + // We got the release signal before the acquire signal mDeferReleaseConsole = false; - revokeGPU(); hw.releaseScreen(); } if (what & eConsoleReleased) { if (hw.canDraw()) { - revokeGPU(); hw.releaseScreen(); } else { mDeferReleaseConsole = true; @@ -621,9 +553,31 @@ void SurfaceFlinger::handleConsoleEvents() void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { - Mutex::Autolock _l(mStateLock); + 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(); + } + } +} - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; +void SurfaceFlinger::handleTransactionLocked( + uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers) +{ + const LayerVector& currentLayers(mCurrentState.layersSortedByZ); const size_t count = currentLayers.size(); /* @@ -634,19 +588,13 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) const bool layersNeedTransaction = transactionFlags & eTraversalNeeded; if (layersNeedTransaction) { for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = currentLayers[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; - - if (flags & Layer::eRestartTransaction) { - // restart the transaction, but back-off a little - layer->setTransactionFlags(eTransactionNeeded); - setTransactionFlags(eTraversalNeeded, ms2ns(8)); - } } } @@ -681,7 +629,6 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) mVisibleRegionsDirty = true; mDirtyRegion.set(hw.bounds()); mFreezeDisplayTime = 0; - mOrientationAnimation->onOrientationChanged(type); } if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { @@ -689,22 +636,26 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) mFreezeDisplay = mCurrentState.freezeDisplay; } - // some layers might have been removed, so - // we need to update the regions they're exposing. - const SortedVector<LayerBase*>& removedLayers(mRemovedLayers); - size_t c = removedLayers.size(); - if (c) { + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added mVisibleRegionsDirty = true; - while (c--) { - mDirtyRegionRemovedLayer.orSelf( - removedLayers[c]->visibleRegionScreen); - } } - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; - if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { - // layers have been added + // 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 @@ -734,7 +685,7 @@ void SurfaceFlinger::computeVisibleRegions( size_t i = currentLayers.size(); while (i--) { - LayerBase* const layer = currentLayers[i]; + const sp<LayerBase>& layer = currentLayers[i]; layer->validateVisibility(planeTransform); // start with the whole surface at its current location @@ -785,7 +736,7 @@ void SurfaceFlinger::computeVisibleRegions( // accumulate to the screen dirty region dirtyRegion.orSelf(dirty); - // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer + // Update aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer aboveOpaqueLayers.orSelf(opaqueRegion); aboveCoveredLayers.orSelf(visibleRegion); @@ -811,7 +762,8 @@ void SurfaceFlinger::computeVisibleRegions( void SurfaceFlinger::commitTransaction() { mDrawingState = mCurrentState; - mTransactionCV.signal(); + mResizeTransationPending = false; + mTransactionCV.broadcast(); } void SurfaceFlinger::handlePageFlip() @@ -837,9 +789,9 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) { bool recomputeVisibleRegions = false; size_t count = currentLayers.size(); - LayerBase* const* layers = currentLayers.array(); + sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->lockPageFlip(recomputeVisibleRegions); } return recomputeVisibleRegions; @@ -850,37 +802,58 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); size_t count = currentLayers.size(); - LayerBase* const* layers = currentLayers.array(); + sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->unlockPageFlip(planeTransform, mDirtyRegion); } } + void SurfaceFlinger::handleRepaint() { - // set the frame buffer - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + if (mInvalidRegion.isEmpty()) { + // nothing to do + return; + } if (UNLIKELY(mDebugRegion)) { debugFlashRegions(); } - // compute the invalid region - mInvalidRegion.orSelf(mDirtyRegion); + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); uint32_t flags = hw.getFlags(); - if (flags & DisplayHardware::BUFFER_PRESERVED) { - // here we assume DisplayHardware::flip()'s implementation - // performs the copy-back optimization. + 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::UPDATE_ON_DEMAND) { - // we need to fully redraw the part that will be updated + 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 + // we need to redraw everything (the whole screen) mDirtyRegion.set(hw.bounds()); mInvalidRegion = mDirtyRegion; } @@ -903,9 +876,9 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty) const SurfaceFlinger& flinger(*this); const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); const size_t count = drawingLayers.size(); - LayerBase const* const* const layers = drawingLayers.array(); + sp<LayerBase> const* const layers = drawingLayers.array(); for (size_t i=0 ; i<count ; ++i) { - LayerBase const * const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; const Region& visibleRegion(layer->visibleRegionScreen); if (!visibleRegion.isEmpty()) { const Region clip(dirty.intersect(visibleRegion)); @@ -920,67 +893,42 @@ void SurfaceFlinger::unlockClients() { const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); const size_t count = drawingLayers.size(); - LayerBase* const* const layers = drawingLayers.array(); + sp<LayerBase> const* const layers = drawingLayers.array(); for (size_t i=0 ; i<count ; ++i) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->finishPageFlip(); } } -void SurfaceFlinger::scheduleBroadcast(Client* client) -{ - if (mLastScheduledBroadcast != client) { - mLastScheduledBroadcast = client; - mScheduledBroadcasts.add(client); - } -} - -void SurfaceFlinger::executeScheduledBroadcasts() -{ - SortedVector<Client*>& list = mScheduledBroadcasts; - size_t count = list.size(); - while (count--) { - per_client_cblk_t* const cblk = list[count]->ctrlblk; - if (cblk->lock.tryLock() == NO_ERROR) { - cblk->cv.broadcast(); - list.removeAt(count); - cblk->lock.unlock(); - } else { - // schedule another round - LOGW("executeScheduledBroadcasts() skipped, " - "contention on the client. We'll try again later..."); - signalDelayedEvent(ms2ns(4)); - } - } - mLastScheduledBroadcast = 0; -} - -void SurfaceFlinger::handleDebugCpu() -{ - Mutex::Autolock _l(mDebugLock); - if (mCpuGauge != 0) - mCpuGauge->sample(); -} - void SurfaceFlinger::debugFlashRegions() { - if (UNLIKELY(!mDirtyRegion.isRect())) { - // TODO: do this only if we don't have preserving - // swapBuffer. If we don't have update-on-demand, - // redraw everything. - composeSurfaces(Region(mDirtyRegion.bounds())); - } + 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); - glColor4x(0x10000, 0, 0x10000, 0x10000); + static int toggle = 0; + toggle = 1 - toggle; + if (toggle) { + glColor4x(0x10000, 0, 0x10000, 0x10000); + } else { + glColor4x(0x10000, 0x10000, 0, 0x10000); + } - Rect r; - Region::iterator iterator(mDirtyRegion); - while (iterator.iterate(&r)) { + 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 }, @@ -990,10 +938,12 @@ void SurfaceFlinger::debugFlashRegions() glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } - - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - hw.flip(mDirtyRegion.merge(mInvalidRegion)); - mInvalidRegion.clear(); + + if (mInvalidRegion.isEmpty()) { + mDirtyRegion.dump("mDirtyRegion"); + mInvalidRegion.dump("mInvalidRegion"); + } + hw.flip(mInvalidRegion); if (mDebugRegion > 1) usleep(mDebugRegion * 1000); @@ -1017,9 +967,10 @@ void SurfaceFlinger::drawWormhole() const if (LIKELY(!mDebugBackground)) { glClearColorx(0,0,0,0); - Rect r; - Region::iterator iterator(region); - while (iterator.iterate(&r)) { + 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); @@ -1037,9 +988,10 @@ void SurfaceFlinger::drawWormhole() const glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1); - Rect r; - Region::iterator iterator(region); - while (iterator.iterate(&r)) { + 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); @@ -1065,7 +1017,7 @@ void SurfaceFlinger::debugShowFPS() const // XXX: mFPS has the value we want } -status_t SurfaceFlinger::addLayer(LayerBase* layer) +status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); addLayer_l(layer); @@ -1073,91 +1025,77 @@ status_t SurfaceFlinger::addLayer(LayerBase* layer) return NO_ERROR; } -status_t SurfaceFlinger::removeLayer(LayerBase* layer) +status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); - removeLayer_l(layer); - setTransactionFlags(eTransactionNeeded); - return NO_ERROR; + status_t err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) + setTransactionFlags(eTransactionNeeded); + return err; } -status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer) +status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) { layer->forceVisibilityTransaction(); setTransactionFlags(eTraversalNeeded); return NO_ERROR; } -status_t SurfaceFlinger::addLayer_l(LayerBase* layer) +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) { + if (layer == 0) + return BAD_VALUE; ssize_t i = mCurrentState.layersSortedByZ.add( layer, &LayerBase::compareCurrentStateZ); - LayerBaseClient* lbc = LayerBase::dynamicCast<LayerBaseClient*>(layer); - if (lbc) { + sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { mLayerMap.add(lbc->serverIndex(), lbc); } - mRemovedLayers.remove(layer); return NO_ERROR; } -status_t SurfaceFlinger::removeLayer_l(LayerBase* layerBase) +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) { ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); if (index >= 0) { - mRemovedLayers.add(layerBase); - LayerBaseClient* layer = LayerBase::dynamicCast<LayerBaseClient*>(layerBase); - if (layer) { + 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 destroySurface, - // destroyclient and destroySurface-from-a-transaction. - return (index == NAME_NOT_FOUND) ? status_t(NO_ERROR) : index; + // 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() { - // Destroy layers that were removed - destroy_all_removed_layers_l(); - // free resources associated with disconnected clients - SortedVector<Client*>& scheduledBroadcasts(mScheduledBroadcasts); - Vector<Client*>& disconnectedClients(mDisconnectedClients); + Vector< sp<Client> >& disconnectedClients(mDisconnectedClients); const size_t count = disconnectedClients.size(); for (size_t i=0 ; i<count ; i++) { - Client* client = disconnectedClients[i]; - // if this client is the scheduled broadcast list, - // remove it from there (and we don't need to signal it - // since it is dead). - int32_t index = scheduledBroadcasts.indexOf(client); - if (index >= 0) { - scheduledBroadcasts.removeItemsAt(index); - } + sp<Client> client = disconnectedClients[i]; mTokens.release(client->cid); - delete client; } disconnectedClients.clear(); } -void SurfaceFlinger::destroy_all_removed_layers_l() -{ - size_t c = mRemovedLayers.size(); - while (c--) { - LayerBase* const removed_layer = mRemovedLayers[c]; - - LOGE_IF(mCurrentState.layersSortedByZ.indexOf(removed_layer) >= 0, - "layer %p removed but still in the current state list", - removed_layer); - - delete removed_layer; - } - mRemovedLayers.clear(); -} - - uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { return android_atomic_and(~flags, &mTransactionFlags) & flags; @@ -1185,6 +1123,20 @@ 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; + } + } } } @@ -1198,7 +1150,7 @@ status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) setTransactionFlags(eTransactionNeeded); // flags is intended to communicate some sort of animation behavior - // (for instance fadding) + // (for instance fading) return NO_ERROR; } @@ -1212,7 +1164,7 @@ status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) setTransactionFlags(eTransactionNeeded); // flags is intended to communicate some sort of animation behavior - // (for instance fadding) + // (for instance fading) return NO_ERROR; } @@ -1241,7 +1193,7 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, DisplayID d, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - LayerBaseClient* layer = 0; + sp<LayerBaseClient> layer; sp<LayerBaseClient::Surface> surfaceHandle; if (int32_t(w|h) < 0) { @@ -1251,14 +1203,14 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, } Mutex::Autolock _l(mStateLock); - Client* const c = mClientsMap.valueFor(clientId); - if (UNLIKELY(!c)) { + 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 = c->generateId(pid); + int32_t id = client->generateId(pid); if (uint32_t(id) >= NUM_LAYERS_MAX) { LOGE("createSurface() failed, generateId = %d", id); return surfaceHandle; @@ -1267,32 +1219,40 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, switch (flags & eFXSurfaceMask) { case eFXSurfaceNormal: if (UNLIKELY(flags & ePushBuffers)) { - layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags); + layer = createPushBuffersSurfaceLocked(client, d, id, + w, h, flags); } else { - layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); + layer = createNormalSurfaceLocked(client, d, id, + w, h, flags, format); } break; case eFXSurfaceBlur: - layer = createBlurSurfaceLocked(c, d, id, w, h, flags); + layer = createBlurSurfaceLocked(client, d, id, w, h, flags); break; case eFXSurfaceDim: - layer = createDimSurfaceLocked(c, d, id, w, h, flags); + layer = createDimSurfaceLocked(client, d, id, w, h, flags); break; } - if (layer) { + if (layer != 0) { setTransactionFlags(eTransactionNeeded); surfaceHandle = layer->getSurface(); - if (surfaceHandle != 0) - surfaceHandle->getSurfaceData(params); + if (surfaceHandle != 0) { + params->token = surfaceHandle->getToken(); + params->identity = surfaceHandle->getIdentity(); + params->width = w; + params->height = h; + params->format = format; + } } return surfaceHandle; } -LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( - Client* client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) +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 @@ -1305,57 +1265,102 @@ LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( break; } - Layer* layer = new Layer(this, display, client, id); - status_t err = layer->setBuffers(client, w, h, format, flags); + 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)); - delete layer; - return 0; + layer.clear(); } return layer; } -LayerBaseClient* SurfaceFlinger::createBlurSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerBlur* layer = new LayerBlur(this, display, client, id); + sp<LayerBlur> layer = new LayerBlur(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -LayerBaseClient* SurfaceFlinger::createDimSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerDim* layer = new LayerDim(this, display, client, id); + sp<LayerDim> layer = new LayerDim(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -LayerBaseClient* SurfaceFlinger::createPushBuffersSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerBuffer* layer = new LayerBuffer(this, display, client, id); + sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -status_t SurfaceFlinger::destroySurface(SurfaceID index) +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); - LayerBaseClient* const layer = getLayerUser_l(index); - status_t err = removeLayer_l(layer); - if (err < 0) - return err; - setTransactionFlags(eTransactionNeeded); + 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; } @@ -1369,18 +1374,9 @@ status_t SurfaceFlinger::setClientState( cid <<= 16; for (int i=0 ; i<count ; i++) { const layer_state_t& s = states[i]; - LayerBaseClient* layer = getLayerUser_l(s.surface | cid); - if (layer) { + sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid)); + if (layer != 0) { const uint32_t what = s.what; - // check if it has been destroyed first - if (what & eDestroyed) { - if (removeLayer_l(layer) == NO_ERROR) { - flags |= eTransactionNeeded; - // we skip everything else... well, no, not really - // we skip ONLY that transaction. - continue; - } - } if (what & ePositionChanged) { if (layer->setPosition(s.x, s.y)) flags |= eTraversalNeeded; @@ -1395,8 +1391,10 @@ status_t SurfaceFlinger::setClientState( } } if (what & eSizeChanged) { - if (layer->setSize(s.w, s.h)) + 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))) @@ -1422,9 +1420,10 @@ status_t SurfaceFlinger::setClientState( return NO_ERROR; } -LayerBaseClient* SurfaceFlinger::getLayerUser_l(SurfaceID s) const +sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const { - return mLayerMap.valueFor(s); + sp<LayerBaseClient> layer = mLayerMap.valueFor(s); + return layer; } void SurfaceFlinger::screenReleased(int dpy) @@ -1446,20 +1445,40 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) const size_t SIZE = 1024; char buffer[SIZE]; String8 result; - if (checkCallingPermission( - String16("android.permission.DUMP")) == false) - { // not allowed + 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 { - Mutex::Autolock _l(mStateLock); + + // 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++) { - Client* client = mClientsMap.valueAt(i); + sp<Client> client = mClientsMap.valueAt(i); sprintf(name, " Client (id=0x%08x)", client->cid); client->dump(name); } @@ -1467,51 +1486,66 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) const size_t count = currentLayers.size(); for (size_t i=0 ; i<count ; i++) { /*** LayerBase ***/ - LayerBase const * const layer = currentLayers[i]; + 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, invalidate=%1d, " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - layer->getTypeID(), layer, + layer->getTypeID(), layer.get(), s.z, layer->tx(), layer->ty(), s.w, s.h, - layer->needsBlending(), layer->contentDirty, + layer->needsBlending(), layer->needsDithering(), + layer->contentDirty, s.alpha, s.flags, s.transform[0], s.transform[1], s.transform[2], s.transform[3]); result.append(buffer); buffer[0] = 0; /*** LayerBaseClient ***/ - LayerBaseClient* const lbc = - LayerBase::dynamicCast<LayerBaseClient*>((LayerBase*)layer); - if (lbc) { + sp<LayerBaseClient> lbc = + LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + sp<Client> client(lbc->client.promote()); snprintf(buffer, SIZE, " " "id=0x%08x, client=0x%08x, identity=%u\n", - lbc->clientIndex(), lbc->client ? lbc->client->cid : 0, + lbc->clientIndex(), client.get() ? client->cid : 0, lbc->getIdentity()); + + result.append(buffer); + buffer[0] = 0; } - result.append(buffer); - buffer[0] = 0; /*** Layer ***/ - Layer* const l = LayerBase::dynamicCast<Layer*>((LayerBase*)layer); - if (l) { - const LayerBitmap& buf0(l->getBuffer(0)); - const LayerBitmap& buf1(l->getBuffer(1)); + 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], mTextureName=%d," - " freezeLock=%p, swapState=0x%08x\n", + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," + " freezeLock=%p, dq-q-time=%u us\n", l->pixelFormat(), - buf0.width(), buf0.height(), buf0.stride(), - buf1.width(), buf1.height(), buf1.stride(), - l->getTextureName(), l->getFreezeLock().get(), - l->lcblk->swapState); + w0, h0, s0, w1, h1, s1, + l->getFreezeLock().get(), stats.totalTime); + result.append(buffer); + buffer[0] = 0; } - result.append(buffer); - buffer[0] = 0; s.transparentRegion.dump(result, "transparentRegion"); layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); @@ -1523,19 +1557,28 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) mFreezeDisplay?"yes":"no", mFreezeCount, mCurrentState.orientation, hw.canDraw()); result.append(buffer); - - sp<AllocatorInterface> allocator; - if (mGPU != 0) { - snprintf(buffer, SIZE, " GPU owner: %d\n", mGPU->getOwner()); + 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); - allocator = mGPU->getAllocator(); - if (allocator != 0) { - allocator->dump(result, "GPU Allocator"); - } } - allocator = mSurfaceHeapManager->getAllocator(NATIVE_MEMORY_TYPE_PMEM); - if (allocator != 0) { - allocator->dump(result, "PMEM Allocator"); + 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()); @@ -1553,57 +1596,34 @@ status_t SurfaceFlinger::onTransact( case FREEZE_DISPLAY: case UNFREEZE_DISPLAY: case BOOT_FINISHED: - case REVOKE_GPU: { // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - LOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } + 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) { - // HARDWARE_TEST stuff... - if (UNLIKELY(checkCallingPermission( - String16("android.permission.HARDWARE_TEST")) == false)) - { // not allowed - LOGE("Permission Denial: pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); + 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 - n = data.readInt32(); - mDebugCpu = n ? 1 : 0; - if (mDebugCpu) { - if (mCpuGauge == 0) { - mCpuGauge = new CPUGauge(this, ms2ns(500)); - } - } else { - if (mCpuGauge != 0) { - mCpuGauge->requestExitAndWait(); - Mutex::Autolock _l(mDebugLock); - mCpuGauge.clear(); - } - } + case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE return NO_ERROR; - case 1001: // SHOW_FPS - n = data.readInt32(); - mDebugFps = n ? 1 : 0; + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE return NO_ERROR; case 1002: // SHOW_UPDATES n = data.readInt32(); @@ -1618,23 +1638,17 @@ status_t SurfaceFlinger::onTransact( const DisplayHardware& hw(graphicPlane(0).displayHardware()); mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe signalEvent(); - } - return NO_ERROR; - case 1005: // ask GPU revoke - if (mGPU != 0) { - mGPU->friendlyRevoke(); - } return NO_ERROR; - case 1006: // revoke GPU - if (mGPU != 0) { - mGPU->unconditionalRevoke(); - } + } + case 1005:{ // force transaction + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); return NO_ERROR; + } case 1007: // set mFreezeCount mFreezeCount = data.readInt32(); return NO_ERROR; case 1010: // interrogate. - reply->writeInt32(mDebugCpu); + reply->writeInt32(0); reply->writeInt32(0); reply->writeInt32(mDebugRegion); reply->writeInt32(mDebugBackground); @@ -1658,30 +1672,24 @@ status_t SurfaceFlinger::onTransact( Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) { - mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(); const int pgsize = getpagesize(); - const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); - mCblkHeap = new MemoryDealer(cblksize); - mCblkMemory = mCblkHeap->allocate(cblksize); - if (mCblkMemory != 0) { - ctrlblk = static_cast<per_client_cblk_t *>(mCblkMemory->pointer()); - if (ctrlblk) { // construct the shared structure in-place. - new(ctrlblk) per_client_cblk_t; - } + 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) { - const int pgsize = getpagesize(); - ctrlblk->~per_client_cblk_t(); // destroy our shared-structure. + ctrlblk->~SharedClient(); // destroy our shared-structure. } } -const sp<SurfaceHeapManager>& Client::getSurfaceHeapManager() const { - return mFlinger->getSurfaceHeapManager(); -} - int32_t Client::generateId(int pid) { const uint32_t i = clz( ~mBitmap ); @@ -1693,13 +1701,15 @@ int32_t Client::generateId(int pid) mBitmap |= 1<<(31-i); return i; } -status_t Client::bindLayer(LayerBaseClient* layer, int32_t id) + +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)); @@ -1709,27 +1719,18 @@ void Client::free(int32_t id) } } -sp<MemoryDealer> Client::createAllocator(uint32_t flags) -{ - sp<MemoryDealer> allocator; - allocator = getSurfaceHeapManager()->createHeap( - flags, getClientPid(), mSharedHeapAllocator); - return allocator; -} - bool Client::isValid(int32_t i) const { return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); } -const uint8_t* Client::inUseArray() const { - return mInUse.array(); -} -size_t Client::numActiveLayers() const { - return mInUse.size(); -} -LayerBaseClient* Client::getLayerUser(int32_t i) const { + +sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { + sp<LayerBaseClient> lbc; ssize_t idx = mInUse.indexOf(uint8_t(i)); - if (idx<0) return 0; - return mLayers[idx]; + 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) @@ -1741,7 +1742,7 @@ void Client::dump(const char* what) #pragma mark - #endif -BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemory>& cblk) +BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk) : mId(cid), mFlinger(flinger), mCblk(cblk) { } @@ -1751,8 +1752,8 @@ BClient::~BClient() { mFlinger->destroyConnection(mId); } -void BClient::getControlBlocks(sp<IMemory>* ctrl) const { - *ctrl = mCblk; +sp<IMemoryHeap> BClient::getControlBlock() const { + return mCblk; } sp<ISurface> BClient::createSurface( @@ -1766,7 +1767,7 @@ sp<ISurface> BClient::createSurface( status_t BClient::destroySurface(SurfaceID sid) { sid |= (mId << 16); // add the client-part to id - return mFlinger->destroySurface(sid); + return mFlinger->removeSurface(sid); } status_t BClient::setState(int32_t count, const layer_state_t* states) @@ -1866,6 +1867,10 @@ const Transform& GraphicPlane::transform() const { return mGlobalTransform; } +EGLDisplay GraphicPlane::getEGLDisplay() const { + return mHw->getEGLDisplay(); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index 0d63e1d..f9bfe6c 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -25,21 +25,24 @@ #include <utils/threads.h> #include <utils/Atomic.h> #include <utils/Errors.h> -#include <utils/MemoryDealer.h> +#include <utils/RefBase.h> + +#include <binder/IMemory.h> +#include <binder/Permission.h> #include <ui/PixelFormat.h> #include <ui/ISurfaceComposer.h> #include <ui/ISurfaceFlingerClient.h> -#include <private/ui/SharedState.h> +#include <private/ui/SharedBufferStack.h> #include <private/ui/LayerState.h> -#include <private/ui/SurfaceFlingerSynchro.h> #include "Barrier.h" -#include "CPUGauge.h" #include "Layer.h" #include "Tokenizer.h" +#include "MessageQueue.h" + struct copybit_device_t; struct overlay_device_t; @@ -51,13 +54,8 @@ class Client; class BClient; class DisplayHardware; class FreezeLock; -class GPUHardwareInterface; -class IGPUCallback; class Layer; class LayerBuffer; -class LayerOrientationAnim; -class OrientationAnimation; -class SurfaceHeapManager; typedef int32_t ClientID; @@ -66,7 +64,7 @@ typedef int32_t ClientID; // --------------------------------------------------------------------------- -class Client +class Client : public RefBase { public: Client(ClientID cid, const sp<SurfaceFlinger>& flinger); @@ -74,35 +72,34 @@ public: int32_t generateId(int pid); void free(int32_t id); - status_t bindLayer(LayerBaseClient* layer, int32_t id); - sp<MemoryDealer> createAllocator(uint32_t memory_type); + status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id); inline bool isValid(int32_t i) const; - inline const uint8_t* inUseArray() const; - inline size_t numActiveLayers() const; - LayerBaseClient* getLayerUser(int32_t i) const; - const Vector<LayerBaseClient*>& getLayers() const { return mLayers; } - const sp<IMemory>& controlBlockMemory() const { return mCblkMemory; } + sp<LayerBaseClient> getLayerUser(int32_t i) const; void dump(const char* what); - const sp<SurfaceHeapManager>& getSurfaceHeapManager() const; + + const Vector< wp<LayerBaseClient> >& getLayers() const { + return mLayers; + } + + const sp<IMemoryHeap>& getControlBlockMemory() const { + return mCblkHeap; + } // pointer to this client's control block - per_client_cblk_t* ctrlblk; + SharedClient* ctrlblk; ClientID cid; private: - int getClientPid() const { return mPid; } + int getClientPid() const { return mPid; } - int mPid; - uint32_t mBitmap; - SortedVector<uint8_t> mInUse; - Vector<LayerBaseClient*> mLayers; - sp<MemoryDealer> mCblkHeap; - sp<SurfaceFlinger> mFlinger; - sp<MemoryDealer> mSharedHeapAllocator; - sp<MemoryDealer> mPMemAllocator; - sp<IMemory> mCblkMemory; + int mPid; + uint32_t mBitmap; + SortedVector<uint8_t> mInUse; + Vector< wp<LayerBaseClient> > mLayers; + sp<IMemoryHeap> mCblkHeap; + sp<SurfaceFlinger> mFlinger; }; // --------------------------------------------------------------------------- @@ -125,6 +122,8 @@ public: const DisplayHardware& displayHardware() const; const Transform& transform() const; + EGLDisplay getEGLDisplay() const; + private: GraphicPlane(const GraphicPlane&); GraphicPlane operator = (const GraphicPlane&); @@ -160,7 +159,7 @@ public: // ISurfaceComposer interface virtual sp<ISurfaceFlingerClient> createConnection(); - virtual sp<IMemory> getCblk() const; + virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); virtual void openGlobalTransaction(); virtual void closeGlobalTransaction(); @@ -168,56 +167,52 @@ public: virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); virtual void signal() const; - virtual status_t requestGPU(const sp<IGPUCallback>& callback, - gpu_info_t* gpu); - virtual status_t revokeGPU(); void screenReleased(DisplayID dpy); void screenAcquired(DisplayID dpy); - const sp<SurfaceHeapManager>& getSurfaceHeapManager() const { - return mSurfaceHeapManager; - } - - const sp<GPUHardwareInterface>& getGPU() const { - return mGPU; - } - - copybit_device_t* getBlitEngine() const; overlay_control_device_t* getOverlayEngine() const; - status_t removeLayer(LayerBase* layer); - status_t addLayer(LayerBase* layer); - status_t invalidateLayerVisibility(LayerBase* layer); + 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, ISurfaceFlingerClient::surface_data_t* params, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); - LayerBaseClient* createNormalSurfaceLocked(Client* client, DisplayID display, - int32_t id, 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); - LayerBaseClient* createBlurSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - LayerBaseClient* createDimSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - LayerBaseClient* createPushBuffersSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - status_t destroySurface(SurfaceID surface_id); - status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + 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 { @@ -225,15 +220,15 @@ private: inline LayerVector() { } LayerVector(const LayerVector&); inline size_t size() const { return layers.size(); } - inline LayerBase*const* array() const { return layers.array(); } - ssize_t add(LayerBase*, Vector<LayerBase*>::compar_t); - ssize_t remove(LayerBase*); - ssize_t reorder(LayerBase*, Vector<LayerBase*>::compar_t); - ssize_t indexOf(LayerBase* key, size_t guess=0) const; - inline LayerBase* operator [] (size_t i) const { return layers[i]; } + 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<LayerBase*, size_t> lookup; - Vector<LayerBase*> layers; + KeyedVector< sp<LayerBase> , size_t> lookup; + Vector< sp<LayerBase> > layers; }; struct State { @@ -247,38 +242,26 @@ private: uint8_t freezeDisplay; }; - class DelayedTransaction : public Thread - { - friend class SurfaceFlinger; - sp<SurfaceFlinger> mFlinger; - nsecs_t mDelay; - public: - DelayedTransaction(const sp<SurfaceFlinger>& flinger, nsecs_t delay) - : Thread(false), mFlinger(flinger), mDelay(delay) { - } - virtual bool threadLoop() { - usleep(mDelay / 1000); - if (android_atomic_and(~1, - &mFlinger->mDeplayedTransactionPending) == 1) { - mFlinger->signalEvent(); - } - return false; - } - }; - 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, @@ -289,19 +272,16 @@ private: bool lockPageFlip(const LayerVector& currentLayers); void unlockPageFlip(const LayerVector& currentLayers); void handleRepaint(); - void handleDebugCpu(); - void scheduleBroadcast(Client* client); - void executeScheduledBroadcasts(); void postFramebuffer(); void composeSurfaces(const Region& dirty); void unlockClients(); void destroyConnection(ClientID cid); - LayerBaseClient* getLayerUser_l(SurfaceID index) const; - status_t addLayer_l(LayerBase* layer); - status_t removeLayer_l(LayerBase* layer); - void destroy_all_removed_layers_l(); + 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); @@ -315,7 +295,7 @@ private: inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } inline bool hasFreezeRequest() const { return mFreezeDisplay; } inline bool isFrozen() const { - return mFreezeDisplay || mFreezeCount>0; + return (mFreezeDisplay || mFreezeCount>0) && mBootFinished; } @@ -323,6 +303,11 @@ private: void debugShowFPS() const; void drawWormhole() const; + + mutable MessageQueue mEventQueue; + + + // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; @@ -330,23 +315,24 @@ private: 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, Client*> mClientsMap; - DefaultKeyedVector<SurfaceID, LayerBaseClient*> mLayerMap; + DefaultKeyedVector<ClientID, sp<Client> > mClientsMap; + DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap; GraphicPlane mGraphicPlanes[1]; - SortedVector<LayerBase*> mRemovedLayers; - Vector<Client*> mDisconnectedClients; + bool mLayersRemoved; + Vector< sp<Client> > mDisconnectedClients; // constant members (no synchronization needed for access) - sp<MemoryDealer> mServerHeap; - sp<IMemory> mServerCblkMemory; + sp<IMemoryHeap> mServerHeap; surface_flinger_cblk_t* mServerCblk; - sp<SurfaceHeapManager> mSurfaceHeapManager; - sp<GPUHardwareInterface> mGPU; GLuint mWormholeTexName; nsecs_t mBootTime; + Permission mHardwareTest; + Permission mAccessSurfaceFlinger; + Permission mDump; // Can only accessed from the main thread, these members // don't need synchronization @@ -354,30 +340,23 @@ private: Region mDirtyRegionRemovedLayer; Region mInvalidRegion; Region mWormholeRegion; - Client* mLastScheduledBroadcast; - SortedVector<Client*> mScheduledBroadcasts; bool mVisibleRegionsDirty; bool mDeferReleaseConsole; bool mFreezeDisplay; int32_t mFreezeCount; nsecs_t mFreezeDisplayTime; - friend class OrientationAnimation; - OrientationAnimation* mOrientationAnimation; - - // access protected by mDebugLock - mutable Mutex mDebugLock; - sp<CPUGauge> mCpuGauge; // don't use a lock for these, we don't care int mDebugRegion; - int mDebugCpu; - int mDebugFps; 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; - mutable SurfaceFlingerSynchro mSyncObject; - volatile int32_t mDeplayedTransactionPending; // atomic variables enum { @@ -410,11 +389,11 @@ class BClient : public BnSurfaceFlingerClient { public: BClient(SurfaceFlinger *flinger, ClientID cid, - const sp<IMemory>& cblk); + const sp<IMemoryHeap>& cblk); ~BClient(); // ISurfaceFlingerClient interface - virtual void getControlBlocks(sp<IMemory>* ctrl) const; + virtual sp<IMemoryHeap> getControlBlock() const; virtual sp<ISurface> createSurface( surface_data_t* params, int pid, @@ -427,7 +406,7 @@ public: private: ClientID mId; SurfaceFlinger* mFlinger; - sp<IMemory> mCblk; + sp<IMemoryHeap> mCblk; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp index ef51d6a..be3a239 100644 --- a/libs/surfaceflinger/Tokenizer.cpp +++ b/libs/surfaceflinger/Tokenizer.cpp @@ -162,9 +162,10 @@ void Tokenizer::dump() const { const run_t* ranges = mRanges.array(); const size_t c = mRanges.size(); - printf("Tokenizer (%p, size = %lu)\n", this, c); + printf("Tokenizer (%p, size = %d)\n", this, int(c)); for (size_t i=0 ; i<c ; i++) { - printf("%lu: (%u, %u)\n", i, ranges[i].first, ranges[i].length); + printf("%u: (%u, %u)\n", i, + uint32_t(ranges[i].first), uint32_t(ranges[i].length)); } } diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp index e8b0f45..1501536 100644 --- a/libs/surfaceflinger/Transform.cpp +++ b/libs/surfaceflinger/Transform.cpp @@ -177,10 +177,10 @@ Region Transform::transform(const Region& reg) const Region out; if (UNLIKELY(transformed())) { if (LIKELY(preserveRects())) { - Rect r; - Region::iterator iterator(reg); - while (iterator.iterate(&r)) { - out.orSelf(transform(r)); + 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())); diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h index 4c4528e..78f5c19 100644 --- a/libs/surfaceflinger/Transform.h +++ b/libs/surfaceflinger/Transform.h @@ -50,6 +50,14 @@ public: ROT_INVALID = 0x80000000 }; + enum type_mask { + IDENTITY = 0, + TRANSLATE = 0x1, + SCALE = 0x2, + AFFINE = 0x4, + PERSPECTIVE = 0x8 + }; + bool transformed() const; int32_t getOrientation() const; bool preserveRects() const; diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp deleted file mode 100644 index 5f633bd..0000000 --- a/libs/surfaceflinger/VRamHeap.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "SurfaceFlinger" - -#include <stdlib.h> -#include <stdio.h> -#include <stdint.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <math.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> - -#include <cutils/log.h> -#include <cutils/properties.h> - -#include <utils/MemoryDealer.h> -#include <utils/MemoryBase.h> -#include <utils/MemoryHeapPmem.h> -#include <utils/MemoryHeapBase.h> - -#include <EGL/eglnatives.h> - -#include "GPUHardware/GPUHardware.h" -#include "SurfaceFlinger.h" -#include "VRamHeap.h" - -#if HAVE_ANDROID_OS -#include <linux/android_pmem.h> -#endif - - -namespace android { - -// --------------------------------------------------------------------------- - -/* - * Amount of memory we reserve for surface, per client in PMEM - * (PMEM is used for 2D acceleration) - * 8 MB of address space per client should be enough. - */ -static const int PMEM_SIZE = int(8 * 1024 * 1024); - -int SurfaceHeapManager::global_pmem_heap = 0; - -// --------------------------------------------------------------------------- - -SurfaceHeapManager::SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, - size_t clientHeapSize) - : mFlinger(flinger), mClientHeapSize(clientHeapSize) -{ - SurfaceHeapManager::global_pmem_heap = 1; -} - -SurfaceHeapManager::~SurfaceHeapManager() -{ -} - -void SurfaceHeapManager::onFirstRef() -{ - if (global_pmem_heap) { - const char* device = "/dev/pmem"; - mPMemHeap = new PMemHeap(device, PMEM_SIZE); - if (mPMemHeap->base() == MAP_FAILED) { - mPMemHeap.clear(); - global_pmem_heap = 0; - } - } -} - -sp<MemoryDealer> SurfaceHeapManager::createHeap( - uint32_t flags, - pid_t client_pid, - const sp<MemoryDealer>& defaultAllocator) -{ - sp<MemoryDealer> dealer; - - if (flags & ISurfaceComposer::eGPU) { - // don't grant GPU memory if GPU is disabled - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.hw", value, "1"); - if (atoi(value) == 0) { - flags &= ~ISurfaceComposer::eGPU; - } - } - - if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) { - // FIXME: this is msm7201A specific, where gpu surfaces may not be secure - if (!(flags & ISurfaceComposer::eSecure)) { - // if GPU doesn't work, we try eHardware - flags |= ISurfaceComposer::eHardware; - // asked for GPU memory, try that first - dealer = mFlinger->getGPU()->request(client_pid); - } - } - - if (dealer == NULL) { - if (defaultAllocator != NULL) - // if a default allocator is given, use that - dealer = defaultAllocator; - } - - if (dealer == NULL) { - // always try h/w accelerated memory first - if (global_pmem_heap) { - const sp<PMemHeap>& heap(mPMemHeap); - if (dealer == NULL && heap != NULL) { - dealer = new MemoryDealer( - heap->createClientHeap(), - heap->getAllocator()); - } - } - } - - if (dealer == NULL) { - // return the ashmem allocator (software rendering) - dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap"); - } - return dealer; -} - -sp<SimpleBestFitAllocator> SurfaceHeapManager::getAllocator(int type) const -{ - Mutex::Autolock _l(mLock); - sp<SimpleBestFitAllocator> allocator; - - // this is only used for debugging - switch (type) { - case NATIVE_MEMORY_TYPE_PMEM: - if (mPMemHeap != 0) { - allocator = mPMemHeap->getAllocator(); - } - break; - } - return allocator; -} - -// --------------------------------------------------------------------------- - -PMemHeap::PMemHeap(const char* const device, size_t size, size_t reserved) - : MemoryHeapBase(device, size) -{ - //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); - if (base() != MAP_FAILED) { - //LOGD("%s, %u bytes", device, virtualSize()); - if (reserved == 0) - reserved = virtualSize(); - mAllocator = new SimpleBestFitAllocator(reserved); - } -} - -PMemHeap::~PMemHeap() { - //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); -} - -sp<MemoryHeapPmem> PMemHeap::createClientHeap() { - sp<MemoryHeapBase> parentHeap(this); - return new MemoryHeapPmem(parentHeap); -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h deleted file mode 100644 index 9140167..0000000 --- a/libs/surfaceflinger/VRamHeap.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 ANDROID_VRAM_HEAP_H -#define ANDROID_VRAM_HEAP_H - -#include <stdint.h> -#include <sys/types.h> -#include <utils/MemoryDealer.h> - -namespace android { - -// --------------------------------------------------------------------------- - -class PMemHeap; -class MemoryHeapPmem; -class SurfaceFlinger; - -// --------------------------------------------------------------------------- - -class SurfaceHeapManager : public RefBase -{ -public: - SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, size_t clientHeapSize); - virtual ~SurfaceHeapManager(); - virtual void onFirstRef(); - /* use ISurfaceComposer flags eGPU|eHArdware|eSecure */ - sp<MemoryDealer> createHeap(uint32_t flags=0, pid_t client_pid = 0, - const sp<MemoryDealer>& defaultAllocator = 0); - - // used for debugging only... - sp<SimpleBestFitAllocator> getAllocator(int type) const; - -private: - sp<PMemHeap> getHeap(int type) const; - - sp<SurfaceFlinger> mFlinger; - mutable Mutex mLock; - size_t mClientHeapSize; - sp<PMemHeap> mPMemHeap; - static int global_pmem_heap; -}; - -// --------------------------------------------------------------------------- - -class PMemHeap : public MemoryHeapBase -{ -public: - PMemHeap(const char* const vram, - size_t size=0, size_t reserved=0); - virtual ~PMemHeap(); - - virtual const sp<SimpleBestFitAllocator>& getAllocator() const { - return mAllocator; - } - virtual sp<MemoryHeapPmem> createClientHeap(); - -private: - sp<SimpleBestFitAllocator> mAllocator; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_VRAM_HEAP_H diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp index f3c046f..0b9322e 100644 --- a/libs/surfaceflinger/tests/overlays/overlays.cpp +++ b/libs/surfaceflinger/tests/overlays/overlays.cpp @@ -1,6 +1,6 @@ -#include <utils/IPCThreadState.h> -#include <utils/ProcessState.h> -#include <utils/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> #include <utils/Log.h> #include <ui/Surface.h> diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/libs/surfaceflinger/tests/resize/Android.mk new file mode 100644 index 0000000..ef1532f --- /dev/null +++ b/libs/surfaceflinger/tests/resize/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + resize.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-resize + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/tests/resize/resize.cpp b/libs/surfaceflinger/tests/resize/resize.cpp new file mode 100644 index 0000000..21c6ab6 --- /dev/null +++ b/libs/surfaceflinger/tests/resize/resize.cpp @@ -0,0 +1,60 @@ +#include <cutils/memory.h> + +#include <utils/IPCThreadState.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> + +#include <ui/Surface.h> +#include <ui/ISurface.h> +#include <ui/Overlay.h> +#include <ui/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, 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; +} diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 7bbe38b..84aec61 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -4,10 +4,13 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ Camera.cpp \ CameraParameters.cpp \ - EGLDisplaySurface.cpp \ - EGLNativeWindowSurface.cpp \ + EGLUtils.cpp \ EventHub.cpp \ EventRecurrence.cpp \ + FramebufferNativeWindow.cpp \ + GraphicBuffer.cpp \ + GraphicBufferAllocator.cpp \ + GraphicBufferMapper.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ ICamera.cpp \ @@ -22,18 +25,23 @@ LOCAL_SRC_FILES:= \ PixelFormat.cpp \ Rect.cpp \ Region.cpp \ + SharedBufferStack.cpp \ Surface.cpp \ - SurfaceComposerClient.cpp \ - SurfaceFlingerSynchro.cpp + SurfaceComposerClient.cpp LOCAL_SHARED_LIBRARIES := \ - libcorecg \ libcutils \ libutils \ + libEGL \ + libbinder \ libpixelflinger \ libhardware \ libhardware_legacy LOCAL_MODULE:= libui +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -lpthread +endif + include $(BUILD_SHARED_LIBRARY) diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index 5015379..09a36f1 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -19,9 +19,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Camera" #include <utils/Log.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/threads.h> -#include <utils/IMemory.h> +#include <binder/IMemory.h> #include <ui/Surface.h> #include <ui/Camera.h> #include <ui/ICameraService.h> @@ -242,6 +242,14 @@ status_t Camera::autoFocus() return c->autoFocus(); } +status_t Camera::cancelAutoFocus() +{ + LOGV("cancelAutoFocus"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->cancelAutoFocus(); +} + // take a picture status_t Camera::takePicture() { @@ -270,6 +278,15 @@ String8 Camera::getParameters() const return params; } +// send command to camera driver +status_t Camera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) +{ + LOGD("sendCommand"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->sendCommand(cmd, arg1, arg2); +} + void Camera::setListener(const sp<CameraListener>& listener) { Mutex::Autolock _l(mLock); diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp index 6c25836..8f1749d 100644 --- a/libs/ui/CameraParameters.cpp +++ b/libs/ui/CameraParameters.cpp @@ -2,16 +2,16 @@ ** ** 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 +** 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 +** 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 +** 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. */ @@ -23,6 +23,103 @@ #include <ui/CameraParameters.h> namespace android { +// Parameter keys to communicate between camera application and driver. +const char CameraParameters::KEY_PREVIEW_SIZE[] = "preview-size"; +const char CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES[] = "preview-size-values"; +const char CameraParameters::KEY_PREVIEW_FORMAT[] = "preview-format"; +const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values"; +const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate"; +const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values"; +const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size"; +const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values"; +const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format"; +const char CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS[] = "picture-format-values"; +const char CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH[] = "jpeg-thumbnail-width"; +const char CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT[] = "jpeg-thumbnail-height"; +const char CameraParameters::KEY_SUPPORTED_THUMBNAIL_SIZES[] = "jpeg-thumbnail-size-values"; +const char CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY[] = "jpeg-thumbnail-quality"; +const char CameraParameters::KEY_JPEG_QUALITY[] = "jpeg-quality"; +const char CameraParameters::KEY_ROTATION[] = "rotation"; +const char CameraParameters::KEY_GPS_LATITUDE[] = "gps-latitude"; +const char CameraParameters::KEY_GPS_LONGITUDE[] = "gps-longitude"; +const char CameraParameters::KEY_GPS_ALTITUDE[] = "gps-altitude"; +const char CameraParameters::KEY_GPS_TIMESTAMP[] = "gps-timestamp"; +const char CameraParameters::KEY_WHITE_BALANCE[] = "whitebalance"; +const char CameraParameters::KEY_SUPPORTED_WHITE_BALANCE[] = "whitebalance-values"; +const char CameraParameters::KEY_EFFECT[] = "effect"; +const char CameraParameters::KEY_SUPPORTED_EFFECTS[] = "effect-values"; +const char CameraParameters::KEY_ANTIBANDING[] = "antibanding"; +const char CameraParameters::KEY_SUPPORTED_ANTIBANDING[] = "antibanding-values"; +const char CameraParameters::KEY_SCENE_MODE[] = "scene-mode"; +const char CameraParameters::KEY_SUPPORTED_SCENE_MODES[] = "scene-mode-values"; +const char CameraParameters::KEY_FLASH_MODE[] = "flash-mode"; +const char CameraParameters::KEY_SUPPORTED_FLASH_MODES[] = "flash-mode-values"; +const char CameraParameters::KEY_FOCUS_MODE[] = "focus-mode"; +const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values"; + +// Values for white balance settings. +const char CameraParameters::WHITE_BALANCE_AUTO[] = "auto"; +const char CameraParameters::WHITE_BALANCE_INCANDESCENT[] = "incandescent"; +const char CameraParameters::WHITE_BALANCE_FLUORESCENT[] = "fluorescent"; +const char CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT[] = "warm-fluorescent"; +const char CameraParameters::WHITE_BALANCE_DAYLIGHT[] = "daylight"; +const char CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT[] = "cloudy-daylight"; +const char CameraParameters::WHITE_BALANCE_TWILIGHT[] = "twilight"; +const char CameraParameters::WHITE_BALANCE_SHADE[] = "shade"; + +// Values for effect settings. +const char CameraParameters::EFFECT_NONE[] = "none"; +const char CameraParameters::EFFECT_MONO[] = "mono"; +const char CameraParameters::EFFECT_NEGATIVE[] = "negative"; +const char CameraParameters::EFFECT_SOLARIZE[] = "solarize"; +const char CameraParameters::EFFECT_SEPIA[] = "sepia"; +const char CameraParameters::EFFECT_POSTERIZE[] = "posterize"; +const char CameraParameters::EFFECT_WHITEBOARD[] = "whiteboard"; +const char CameraParameters::EFFECT_BLACKBOARD[] = "blackboard"; +const char CameraParameters::EFFECT_AQUA[] = "aqua"; + +// Values for antibanding settings. +const char CameraParameters::ANTIBANDING_AUTO[] = "auto"; +const char CameraParameters::ANTIBANDING_50HZ[] = "50hz"; +const char CameraParameters::ANTIBANDING_60HZ[] = "60hz"; +const char CameraParameters::ANTIBANDING_OFF[] = "off"; + +// Values for flash mode settings. +const char CameraParameters::FLASH_MODE_OFF[] = "off"; +const char CameraParameters::FLASH_MODE_AUTO[] = "auto"; +const char CameraParameters::FLASH_MODE_ON[] = "on"; +const char CameraParameters::FLASH_MODE_RED_EYE[] = "red-eye"; +const char CameraParameters::FLASH_MODE_TORCH[] = "torch"; + +// Values for scene mode settings. +const char CameraParameters::SCENE_MODE_AUTO[] = "auto"; +const char CameraParameters::SCENE_MODE_ACTION[] = "action"; +const char CameraParameters::SCENE_MODE_PORTRAIT[] = "portrait"; +const char CameraParameters::SCENE_MODE_LANDSCAPE[] = "landscape"; +const char CameraParameters::SCENE_MODE_NIGHT[] = "night"; +const char CameraParameters::SCENE_MODE_NIGHT_PORTRAIT[] = "night-portrait"; +const char CameraParameters::SCENE_MODE_THEATRE[] = "theatre"; +const char CameraParameters::SCENE_MODE_BEACH[] = "beach"; +const char CameraParameters::SCENE_MODE_SNOW[] = "snow"; +const char CameraParameters::SCENE_MODE_SUNSET[] = "sunset"; +const char CameraParameters::SCENE_MODE_STEADYPHOTO[] = "steadyphoto"; +const char CameraParameters::SCENE_MODE_FIREWORKS[] = "fireworks"; +const char CameraParameters::SCENE_MODE_SPORTS[] = "sports"; +const char CameraParameters::SCENE_MODE_PARTY[] = "party"; +const char CameraParameters::SCENE_MODE_CANDLELIGHT[] = "candlelight"; + +// Formats for setPreviewFormat and setPictureFormat. +const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp"; +const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp"; +const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv"; +const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565"; +const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg"; + +// Values for focus mode settings. +const char CameraParameters::FOCUS_MODE_AUTO[] = "auto"; +const char CameraParameters::FOCUS_MODE_INFINITY[] = "infinity"; +const char CameraParameters::FOCUS_MODE_MACRO[] = "macro"; +const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed"; static const char* portrait = "portrait"; static const char* landscape = "landscape"; @@ -91,7 +188,7 @@ void CameraParameters::unflatten(const String8 ¶ms) void CameraParameters::set(const char *key, const char *value) { - // XXX i think i can do this with strspn() + // XXX i think i can do this with strspn() if (strchr(key, '=') || strchr(key, ';')) { //XXX LOGE("Key \"%s\"contains invalid character (= or ;)", key); return; @@ -150,7 +247,7 @@ void CameraParameters::setPreviewSize(int width, int height) { char str[32]; sprintf(str, "%dx%d", width, height); - set("preview-size", str); + set(KEY_PREVIEW_SIZE, str); } void CameraParameters::getPreviewSize(int *width, int *height) const @@ -159,7 +256,7 @@ void CameraParameters::getPreviewSize(int *width, int *height) const *height = -1; // Get the current string, if it doesn't exist, leave the -1x-1 - const char *p = get("preview-size"); + const char *p = get(KEY_PREVIEW_SIZE); if (p == 0) return; @@ -172,17 +269,17 @@ void CameraParameters::getPreviewSize(int *width, int *height) const void CameraParameters::setPreviewFrameRate(int fps) { - set("preview-frame-rate", fps); + set(KEY_PREVIEW_FRAME_RATE, fps); } int CameraParameters::getPreviewFrameRate() const { - return getInt("preview-frame-rate"); + return getInt(KEY_PREVIEW_FRAME_RATE); } void CameraParameters::setPreviewFormat(const char *format) { - set("preview-format", format); + set(KEY_PREVIEW_FORMAT, format); } int CameraParameters::getOrientation() const @@ -196,22 +293,22 @@ int CameraParameters::getOrientation() const void CameraParameters::setOrientation(int orientation) { if (orientation == CAMERA_ORIENTATION_PORTRAIT) { - set("preview-format", portrait); + set("orientation", portrait); } else { - set("preview-format", landscape); + set("orientation", landscape); } } const char *CameraParameters::getPreviewFormat() const { - return get("preview-format"); + return get(KEY_PREVIEW_FORMAT); } void CameraParameters::setPictureSize(int width, int height) { char str[32]; sprintf(str, "%dx%d", width, height); - set("picture-size", str); + set(KEY_PICTURE_SIZE, str); } void CameraParameters::getPictureSize(int *width, int *height) const @@ -220,7 +317,7 @@ void CameraParameters::getPictureSize(int *width, int *height) const *height = -1; // Get the current string, if it doesn't exist, leave the -1x-1 - const char *p = get("picture-size"); + const char *p = get(KEY_PICTURE_SIZE); if (p == 0) return; @@ -233,12 +330,12 @@ void CameraParameters::getPictureSize(int *width, int *height) const void CameraParameters::setPictureFormat(const char *format) { - set("picture-format", format); + set(KEY_PICTURE_FORMAT, format); } const char *CameraParameters::getPictureFormat() const { - return get("picture-format"); + return get(KEY_PICTURE_FORMAT); } void CameraParameters::dump() const diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp deleted file mode 100644 index d06c98b..0000000 --- a/libs/ui/EGLDisplaySurface.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* - ** - ** 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 "EGLDisplaySurface" - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/mman.h> - -#include <cutils/log.h> -#include <cutils/atomic.h> -#include <cutils/properties.h> - -#include <hardware/copybit.h> - -#include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <ui/EGLDisplaySurface.h> - -#if HAVE_ANDROID_OS -#include <linux/msm_mdp.h> -#endif - -#include <EGL/egl.h> - -#include <pixelflinger/format.h> - - -// ---------------------------------------------------------------------------- - -egl_native_window_t* android_createDisplaySurface() -{ - egl_native_window_t* s = new android::EGLDisplaySurface(); - s->memory_type = NATIVE_MEMORY_TYPE_GPU; - return s; -} - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -EGLDisplaySurface::EGLDisplaySurface() - : EGLNativeSurface<EGLDisplaySurface>() -{ - egl_native_window_t::version = sizeof(egl_native_window_t); - egl_native_window_t::ident = 0; - egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; - egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; - egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; - egl_native_window_t::connect = 0; - egl_native_window_t::disconnect = 0; - - mFb[0].data = 0; - mFb[1].data = 0; - mBlitEngine = 0; - egl_native_window_t::fd = mapFrameBuffer(); - if (egl_native_window_t::fd >= 0) { - - hw_module_t const* module; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - copybit_open(module, &mBlitEngine); - } - - const float in2mm = 25.4f; - float refreshRate = 1000000000000000LLU / ( - float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) - * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres ) - * mInfo.pixclock); - - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::width = buffer.width; - egl_native_window_t::height = buffer.height; - egl_native_window_t::stride = buffer.stride; - egl_native_window_t::format = buffer.format; - egl_native_window_t::base = intptr_t(mFb[0].data); - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - egl_native_window_t::flags = 0; - egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width; - egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height; - egl_native_window_t::fps = refreshRate; - egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB; - // no error, set the magic word - egl_native_window_t::magic = 0x600913; - } - mSwapCount = -1; - mPageFlipCount = 0; -} - -EGLDisplaySurface::~EGLDisplaySurface() -{ - magic = 0; - copybit_close(mBlitEngine); - mBlitEngine = 0; - close(egl_native_window_t::fd); - munmap(mFb[0].data, mSize); - if (!(mFlags & PAGE_FLIP)) - free((void*)mFb[1].data); -} - -void EGLDisplaySurface::hook_incRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - that->incStrong(that); -} -void EGLDisplaySurface::hook_decRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - that->decStrong(that); -} -uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - return that->swapBuffers(); -} - -void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) -{ - mInfo.reserved[0] = 0x54445055; // "UPDT"; - mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); - mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); -} - -uint32_t EGLDisplaySurface::swapBuffers() -{ -#define SHOW_FPS 0 -#if SHOW_FPS - nsecs_t now = systemTime(); - if (mSwapCount == -1) { - mTime = now; - mSwapCount = 0; - mSleep = 0; - } else { - nsecs_t d = now-mTime; - if (d >= seconds(1)) { - double fps = (mSwapCount * double(seconds(1))) / double(d); - LOGD("%f fps, sleep=%d / frame", - fps, (int)ns2us(mSleep / mSwapCount)); - mSwapCount = 0; - mTime = now; - mSleep = 0; - } else { - mSwapCount++; - } - } -#endif - /* If we can't do the page_flip, just copy the back buffer to the front */ - if (!(mFlags & PAGE_FLIP)) { - memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2); - return 0; - } - - // do the actual flip - mIndex = 1 - mIndex; - mInfo.activate = FB_ACTIVATE_VBL; - mInfo.yoffset = mIndex ? mInfo.yres : 0; - if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) { - LOGE("FBIOPUT_VSCREENINFO failed"); - return 0; - } - - /* - * this is a monstrous hack: Because the h/w accelerator is not able - * to render directly into the framebuffer, we need to copy its - * internal framebuffer out to the fb. - * oem[0] is used to access the fd of internal fb. - * All this is needed only in standalone mode, in SurfaceFlinger mode - * we control where the GPU renders. - * We do this only if we have copybit, since this hack is needed only - * with msm7k. - */ - if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) { - copybit_device_t *copybit = mBlitEngine; - copybit_rect_t sdrect = { 0, 0, - egl_native_window_t::width, egl_native_window_t::height }; - copybit_image_t dst = { - egl_native_window_t::width, - egl_native_window_t::height, - egl_native_window_t::format, - egl_native_window_t::offset, - (void*)egl_native_window_t::base, - egl_native_window_t::fd - }; - copybit_image_t src = { - egl_native_window_t::width, - egl_native_window_t::height, - egl_native_window_t::format, // XXX: use proper format - egl_native_window_t::offset, - (void*)egl_native_window_t::base, // XXX: use proper base - egl_native_window_t::oem[0] - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it); - } - - // update the address of the buffer to draw to next - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - -#if SHOW_FPS - mSleep += systemTime()-now; -#endif - - mPageFlipCount++; - - // We don't support screen-size changes for now - return 0; -} - -int32_t EGLDisplaySurface::getPageFlipCount() const -{ - return mPageFlipCount; -} - -void EGLDisplaySurface::copyFrontToBack(const Region& copyback) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t dst = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[1-mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(copyback); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - /* no extra copy needed since we copied back to front instead of - * flipping */ - if (!(mFlags & PAGE_FLIP)) { - return; - } - - Region::iterator iterator(copyback); - if (iterator) { - Rect r; - uint8_t* const screen_src = mFb[ mIndex].data; - uint8_t* const screen_dst = mFb[1-mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - while (iterator.iterate(&r)) { - ssize_t h = r.bottom - r.top; - if (h) { - size_t size = (r.right - r.left) * bpp; - size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp; - uint8_t* s = screen_src + o; - uint8_t* d = screen_dst + o; - if (size == bpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += bpr; - s += bpr; - } while (--h > 0); - } - } - } - } -} - -void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - uint8_t* const screen_src = mFb[ mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - memcpy((char*)dst.base + dst.offset, screen_src, - bpr*egl_native_window_t::height); - } -} - -void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[1-mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - uint8_t* const screen_src = mFb[1-mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - memcpy((char*)dst.base + dst.offset, screen_src, - bpr*egl_native_window_t::height); - } -} - - -status_t EGLDisplaySurface::mapFrameBuffer() -{ - char const * const device_template[] = { - "/dev/graphics/fb%u", - "/dev/fb%u", - 0 }; - int fd = -1; - int i=0; - char name[64]; - while ((fd==-1) && device_template[i]) { - snprintf(name, 64, device_template[i], 0); - fd = open(name, O_RDWR, 0); - i++; - } - if (fd < 0) - return -errno; - - struct fb_fix_screeninfo finfo; - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - struct fb_var_screeninfo info; - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - info.reserved[0] = 0; - info.reserved[1] = 0; - info.reserved[2] = 0; - info.xoffset = 0; - info.yoffset = 0; - info.yres_virtual = info.yres * 2; - info.bits_per_pixel = 16; - /* Explicitly request 5/6/5 */ - info.red.offset = 11; - info.red.length = 5; - info.green.offset = 5; - info.green.length = 6; - info.blue.offset = 0; - info.blue.length = 5; - info.transp.offset = 0; - info.transp.length = 0; - info.activate = FB_ACTIVATE_NOW; - - uint32_t flags = PAGE_FLIP; - if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); - } - - if (info.yres_virtual < info.yres * 2) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", - info.yres_virtual, info.yres*2); - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - int refreshRate = 1000000000000000LLU / - ( - uint64_t( info.upper_margin + info.lower_margin + info.yres ) - * ( info.left_margin + info.right_margin + info.xres ) - * info.pixclock - ); - - if (refreshRate == 0) { - // bleagh, bad info from the driver - refreshRate = 60*1000; // 60 Hz - } - if (int(info.width) <= 0 || int(info.height) <= 0) { - // the driver doesn't return that information - // default to 160 dpi - info.width = ((info.xres * 25.4f)/160.0f + 0.5f); - info.height = ((info.yres * 25.4f)/160.0f + 0.5f); - } - - float xdpi = (info.xres * 25.4f) / info.width; - float ydpi = (info.yres * 25.4f) / info.height; - float fps = refreshRate / 1000.0f; - - LOGI( "using (fd=%d)\n" - "id = %s\n" - "xres = %d px\n" - "yres = %d px\n" - "xres_virtual = %d px\n" - "yres_virtual = %d px\n" - "bpp = %d\n" - "r = %2u:%u\n" - "g = %2u:%u\n" - "b = %2u:%u\n", - fd, - finfo.id, - info.xres, - info.yres, - info.xres_virtual, - info.yres_virtual, - info.bits_per_pixel, - info.red.offset, info.red.length, - info.green.offset, info.green.length, - info.blue.offset, info.blue.length - ); - - LOGI( "width = %d mm (%f dpi)\n" - "height = %d mm (%f dpi)\n" - "refresh rate = %.2f Hz\n", - info.width, xdpi, - info.height, ydpi, - fps - ); - - - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - if (finfo.smem_len <= 0) - return -errno; - - /* - * Open and map the display. - */ - - void* buffer = (uint16_t*) mmap( - 0, finfo.smem_len, - PROT_READ | PROT_WRITE, - MAP_SHARED, - fd, 0); - - if (buffer == MAP_FAILED) - return -errno; - - // at least for now, always clear the fb - memset(buffer, 0, finfo.smem_len); - - uint8_t* offscreen[2]; - offscreen[0] = (uint8_t*)buffer; - if (flags & PAGE_FLIP) { - offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres; - } else { - offscreen[1] = (uint8_t*)malloc(finfo.smem_len); - if (offscreen[1] == 0) { - munmap(buffer, finfo.smem_len); - return NO_MEMORY; - } - } - - mFlags = flags; - mInfo = info; - mFinfo = finfo; - mSize = finfo.smem_len; - mIndex = 0; - for (int i=0 ; i<2 ; i++) { - mFb[i].version = sizeof(GGLSurface); - mFb[i].width = info.xres; - mFb[i].height = info.yres; - mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3); - mFb[i].data = (GGLubyte*)(offscreen[i]); - mFb[i].format = GGL_PIXEL_FORMAT_RGB_565; - } - return fd; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp deleted file mode 100644 index f1071cf..0000000 --- a/libs/ui/EGLNativeWindowSurface.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* -** -** 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 "EGLNativeWindowSurface" - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <cutils/log.h> -#include <cutils/atomic.h> - -#include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> -#include <ui/Rect.h> - -#include <EGL/egl.h> - -#include <pixelflinger/format.h> - -#include <ui/EGLNativeWindowSurface.h> - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -EGLNativeWindowSurface::EGLNativeWindowSurface(const sp<Surface>& surface) - : EGLNativeSurface<EGLNativeWindowSurface>(), - mSurface(surface), mConnected(false) -{ - egl_native_window_t::magic = 0x600913; - egl_native_window_t::version = sizeof(egl_native_window_t); - egl_native_window_t::ident = 0; - egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef; - egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef; - egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers; - egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect; - egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect; - - DisplayInfo dinfo; - SurfaceComposerClient::getDisplayInfo(0, &dinfo); - egl_native_window_t::xdpi = dinfo.xdpi; - egl_native_window_t::ydpi = dinfo.ydpi; - egl_native_window_t::fps = dinfo.fps; - egl_native_window_t::flags= EGL_NATIVES_FLAG_DESTROY_BACKBUFFER; -} - -EGLNativeWindowSurface::~EGLNativeWindowSurface() -{ - disconnect(); - mSurface.clear(); - magic = 0; -} - -void EGLNativeWindowSurface::hook_incRef(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->incStrong(that); -} - -void EGLNativeWindowSurface::hook_decRef(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->decStrong(that); -} - -void EGLNativeWindowSurface::hook_connect(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->connect(); -} - -void EGLNativeWindowSurface::hook_disconnect(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->disconnect(); -} - -uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - return that->swapBuffers(); -} - -void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h) -{ - mSurface->setSwapRectangle(Rect(l, t, l+w, t+h)); -} - -uint32_t EGLNativeWindowSurface::swapBuffers() -{ - const int w = egl_native_window_t::width; - const int h = egl_native_window_t::height; - const sp<Surface>& surface(mSurface); - Surface::SurfaceInfo info; - surface->unlockAndPost(); - surface->lock(&info); - // update the address of the buffer to draw to next - egl_native_window_t::base = intptr_t(info.base); - egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - - // update size if it changed - if (w != int(info.w) || h != int(info.h)) { - egl_native_window_t::width = info.w; - egl_native_window_t::height = info.h; - egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); - egl_native_window_t::format = info.format; - return EGL_NATIVES_FLAG_SIZE_CHANGED; - } - return 0; -} - -void EGLNativeWindowSurface::connect() -{ - if (!mConnected) { - Surface::SurfaceInfo info; - mSurface->lock(&info); - mSurface->setSwapRectangle(Rect(info.w, info.h)); - mConnected = true; - - egl_native_window_t::width = info.w; - egl_native_window_t::height = info.h; - egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); - egl_native_window_t::format = info.format; - egl_native_window_t::base = intptr_t(info.base); - egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - // FIXME: egl_native_window_t::memory_type used to be set from - // mSurface, but we wanted to break this dependency. We set it to - // GPU because the software rendered doesn't care, but the h/w - // accelerator needs it. Eventually, this value should go away - // completely, since memory will be managed by OpenGL. - egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU; - egl_native_window_t::fd = 0; - } -} - -void EGLNativeWindowSurface::disconnect() -{ - if (mConnected) { - mSurface->unlock(); - mConnected = false; - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/libs/ui/EGLUtils.cpp b/libs/ui/EGLUtils.cpp new file mode 100644 index 0000000..1663313 --- /dev/null +++ b/libs/ui/EGLUtils.cpp @@ -0,0 +1,136 @@ +/* + * 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 "EGLUtils" + +#include <cutils/log.h> +#include <utils/Errors.h> + +#include <ui/EGLUtils.h> + +#include <EGL/egl.h> + +#include <private/ui/android_natives_priv.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +const char *EGLUtils::strerror(EGLint err) +{ + switch (err){ + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "UNKNOWN"; + } +} + +status_t EGLUtils::selectConfigForPixelFormat( + EGLDisplay dpy, + EGLint const* attrs, + PixelFormat format, + EGLConfig* outConfig) +{ + EGLint numConfigs = -1, n=0; + + if (!attrs) + return BAD_VALUE; + + if (outConfig == NULL) + return BAD_VALUE; + + int err; + PixelFormatInfo fbFormatInfo; + if ((err = getPixelFormatInfo(PixelFormat(format), &fbFormatInfo)) < 0) { + return err; + } + + // Get all the "potential match" configs... + if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE) + return BAD_VALUE; + + EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs); + if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) { + free(configs); + return BAD_VALUE; + } + + const int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA); + const int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED); + const int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN); + const int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE); + + int i; + EGLConfig config = NULL; + for (i=0 ; i<n ; i++) { + EGLint r,g,b,a; + EGLConfig curr = configs[i]; + eglGetConfigAttrib(dpy, curr, EGL_RED_SIZE, &r); + eglGetConfigAttrib(dpy, curr, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(dpy, curr, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(dpy, curr, EGL_ALPHA_SIZE, &a); + if (fbSzA == a && fbSzR == r && fbSzG == g && fbSzB == b) { + config = curr; + break; + } + } + + free(configs); + + if (i<n) { + *outConfig = config; + return NO_ERROR; + } + + return NAME_NOT_FOUND; +} + +status_t EGLUtils::selectConfigForNativeWindow( + EGLDisplay dpy, + EGLint const* attrs, + EGLNativeWindowType window, + EGLConfig* outConfig) +{ + int err; + int format; + + if (!window) + return BAD_VALUE; + + if ((err = window->query(window, NATIVE_WINDOW_FORMAT, &format)) < 0) { + return err; + } + + return selectConfigForPixelFormat(dpy, attrs, format, outConfig); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 7c2fc8e..e39a357 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -16,13 +16,14 @@ //#define LOG_NDEBUG 0 #include <ui/EventHub.h> +#include <ui/KeycodeLabels.h> #include <hardware_legacy/power.h> #include <cutils/properties.h> -#include <utils/IServiceManager.h> #include <utils/Log.h> #include <utils/Timers.h> -#include <utils.h> +#include <utils/threads.h> +#include <utils/Errors.h> #include <stdlib.h> #include <stdio.h> @@ -58,6 +59,18 @@ #define SEQ_SHIFT 16 #define id_to_index(id) ((id&ID_MASK)+1) +#ifndef ABS_MT_TOUCH_MAJOR +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#endif + +#ifndef ABS_MT_POSITION_X +#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ +#endif + +#ifndef ABS_MT_POSITION_Y +#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ +#endif + namespace android { static const char *WAKE_LOCK_ID = "KeyEvents"; @@ -69,8 +82,8 @@ static inline int max(int v1, int v2) return (v1 > v2) ? v1 : v2; } -EventHub::device_t::device_t(int32_t _id, const char* _path) - : id(_id), path(_path), classes(0) +EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name) + : id(_id), path(_path), name(name), classes(0) , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) { } @@ -83,7 +96,7 @@ EventHub::EventHub(void) : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) , mDevicesById(0), mNumDevicesById(0) , mOpeningDevices(0), mClosingDevices(0) - , mDevices(0), mFDs(0), mFDCount(0) + , mDevices(0), mFDs(0), mFDCount(0), mOpened(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); #ifdef EV_SW @@ -100,11 +113,6 @@ EventHub::~EventHub(void) // we should free stuff here... } -void EventHub::onFirstRef() -{ - mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; -} - status_t EventHub::errorCheck() const { return mError; @@ -239,6 +247,41 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const return 0; } +status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, + int32_t* outKeycode, uint32_t* outFlags) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + + if (device != NULL && device->layoutMap != NULL) { + status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + + if (mHaveFirstKeyboard) { + device = getDevice(mFirstKeyboardId); + + if (device != NULL && device->layoutMap != NULL) { + status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + } + + *outKeycode = 0; + *outFlags = 0; + return NAME_NOT_FOUND; +} + +void EventHub::addExcludedDevice(const char* deviceName) +{ + String8 name(deviceName); + mExcludedDevices.push_back(name); +} + EventHub::device_t* EventHub::getDevice(int32_t deviceId) const { if (deviceId == 0) deviceId = mFirstKeyboardId; @@ -276,7 +319,12 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, // Note that we only allow one caller to getEvent(), so don't need // to do locking here... only when adding/removing devices. - + + if (!mOpened) { + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; + mOpened = true; + } + while(1) { // First, report any devices that had last been added/removed. @@ -474,6 +522,20 @@ int EventHub::open_device(const char *deviceName) //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); name[0] = '\0'; } + + // check to see if the device is on our excluded list + List<String8>::iterator iter = mExcludedDevices.begin(); + List<String8>::iterator end = mExcludedDevices.end(); + for ( ; iter != end; iter++) { + const char* test = *iter; + if (strcmp(name, test) == 0) { + LOGI("ignoring event id %s driver %s\n", deviceName, test); + close(fd); + fd = -1; + return -1; + } + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); location[0] = '\0'; @@ -531,7 +593,7 @@ int EventHub::open_device(const char *deviceName) version >> 16, (version >> 8) & 0xff, version & 0xff); #endif - device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName); + device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); if (device == NULL) { LOGE("out of memory"); return -1; @@ -541,6 +603,8 @@ int EventHub::open_device(const char *deviceName) mFDs[mFDCount].events = POLLIN; // figure out the kinds of events the device reports + + // See if this is a keyboard, and classify it. uint8_t key_bitmask[(KEY_MAX+1)/8]; memset(key_bitmask, 0, sizeof(key_bitmask)); LOGV("Getting keys..."); @@ -552,15 +616,11 @@ int EventHub::open_device(const char *deviceName) for (int i=0; i<((BTN_MISC+7)/8); i++) { if (key_bitmask[i] != 0) { device->classes |= CLASS_KEYBOARD; - // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (test_bit(KEY_Q, key_bitmask)) { - device->classes |= CLASS_ALPHAKEY; - } break; } } if ((device->classes & CLASS_KEYBOARD) != 0) { - device->keyBitmask = new uint8_t[(KEY_MAX+1)/8]; + device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; if (device->keyBitmask != NULL) { memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); } else { @@ -570,6 +630,8 @@ int EventHub::open_device(const char *deviceName) } } } + + // See if this is a trackball. if (test_bit(BTN_MOUSE, key_bitmask)) { uint8_t rel_bitmask[(REL_MAX+1)/8]; memset(rel_bitmask, 0, sizeof(rel_bitmask)); @@ -581,16 +643,22 @@ int EventHub::open_device(const char *deviceName) } } } - if (test_bit(BTN_TOUCH, key_bitmask)) { - uint8_t abs_bitmask[(ABS_MAX+1)/8]; - memset(abs_bitmask, 0, sizeof(abs_bitmask)); - LOGV("Getting absolute controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) - { - if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { - device->classes |= CLASS_TOUCHSCREEN; - } - } + + uint8_t abs_bitmask[(ABS_MAX+1)/8]; + memset(abs_bitmask, 0, sizeof(abs_bitmask)); + LOGV("Getting absolute controllers..."); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); + + // Is this a new modern multi-touch driver? + if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) + && test_bit(ABS_MT_POSITION_X, abs_bitmask) + && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { + device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT; + + // Is this an old style single-touch driver? + } else if (test_bit(BTN_TOUCH, key_bitmask) + && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { + device->classes |= CLASS_TOUCHSCREEN; } #ifdef EV_SW @@ -609,21 +677,15 @@ int EventHub::open_device(const char *deviceName) } #endif - LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); - if ((device->classes&CLASS_KEYBOARD) != 0) { - char devname[101]; - char tmpfn[101]; + char tmpfn[sizeof(name)]; char keylayoutFilename[300]; // a more descriptive name - ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname); - devname[sizeof(devname)-1] = 0; - device->name = devname; + device->name = name; // replace all the spaces with underscores - strcpy(tmpfn, devname); + strcpy(tmpfn, name); for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) *p = '_'; @@ -656,12 +718,29 @@ int EventHub::open_device(const char *deviceName) } char propName[100]; sprintf(propName, "hw.keyboards.%u.devname", publicID); - property_set(propName, devname); + property_set(propName, name); - LOGI("New keyboard: publicID=%d device->id=%d devname='%s' propName='%s' keylayout='%s'\n", - publicID, device->id, devname, propName, keylayoutFilename); + // 'Q' key support = cheap test of whether this is an alpha-capable kbd + if (hasKeycode(device, kKeyCodeQ)) { + device->classes |= CLASS_ALPHAKEY; + } + + // See if this has a DPAD. + if (hasKeycode(device, kKeyCodeDpadUp) && + hasKeycode(device, kKeyCodeDpadDown) && + hasKeycode(device, kKeyCodeDpadLeft) && + hasKeycode(device, kKeyCodeDpadRight) && + hasKeycode(device, kKeyCodeDpadCenter)) { + device->classes |= CLASS_DPAD; + } + + LOGI("New keyboard: publicID=%d device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n", + publicID, device->id, name, propName, keylayoutFilename); } + LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); + LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", deviceName, device, mFDCount, devid, device->classes); @@ -674,6 +753,25 @@ int EventHub::open_device(const char *deviceName) return 0; } +bool EventHub::hasKeycode(device_t* device, int keycode) const +{ + if (device->keyBitmask == NULL || device->layoutMap == NULL) { + return false; + } + + Vector<int32_t> scanCodes; + device->layoutMap->findScancodes(keycode, &scanCodes); + const size_t N = scanCodes.size(); + for (size_t i=0; i<N && i<=KEY_MAX; i++) { + int32_t sc = scanCodes.itemAt(i); + if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { + return true; + } + } + + return false; +} + int EventHub::close_device(const char *deviceName) { AutoMutex _l(mLock); @@ -683,11 +781,21 @@ int EventHub::close_device(const char *deviceName) if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { //LOGD("remove device %d: %s\n", i, deviceName); device_t* device = mDevices[i]; - int count = mFDCount - i - 1; + + LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", + device->path.string(), device->name.string(), device->id, + mNumDevicesById, mFDCount, mFDs[i].fd, device->classes); + + // Clear this device's entry. int index = (device->id&ID_MASK); mDevicesById[index].device = NULL; + + // Close the file descriptor and compact the fd array. + close(mFDs[i].fd); + int count = mFDCount - i - 1; memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); + mFDCount--; #ifdef EV_SW for (int j=0; j<EV_SW; j++) { @@ -700,8 +808,6 @@ int EventHub::close_device(const char *deviceName) device->next = mClosingDevices; mClosingDevices = device; - mFDCount--; - uint32_t publicID; if (device->id == mFirstKeyboardId) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", @@ -718,7 +824,7 @@ int EventHub::close_device(const char *deviceName) return 0; } } - LOGE("remote device: %s not found\n", deviceName); + LOGE("remove device: %s not found\n", deviceName); return -1; } @@ -733,6 +839,7 @@ int EventHub::read_notify(int nfd) int event_pos = 0; struct inotify_event *event; + LOGV("EventHub::read_notify nfd: %d\n", nfd); res = read(nfd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp new file mode 100644 index 0000000..0efba9c --- /dev/null +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -0,0 +1,279 @@ +/* +** +** 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 "FramebufferNativeWindow" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <utils/threads.h> +#include <utils/RefBase.h> + +#include <ui/SurfaceComposerClient.h> +#include <ui/Rect.h> +#include <ui/FramebufferNativeWindow.h> + +#include <EGL/egl.h> + +#include <pixelflinger/format.h> +#include <pixelflinger/pixelflinger.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +#include <private/ui/android_natives_priv.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class NativeBuffer + : public EGLNativeBase< + android_native_buffer_t, + NativeBuffer, + LightRefBase<NativeBuffer> > +{ +public: + NativeBuffer(int w, int h, int f, int u) : BASE() { + android_native_buffer_t::width = w; + android_native_buffer_t::height = h; + android_native_buffer_t::format = f; + android_native_buffer_t::usage = u; + } +private: + friend class LightRefBase<NativeBuffer>; + ~NativeBuffer() { }; // this class cannot be overloaded +}; + + +/* + * This implements the (main) framebuffer management. This class is used + * mostly by SurfaceFlinger, but also by command line GL application. + * + * In fact this is an implementation of android_native_window_t on top of + * the framebuffer. + * + * Currently it is pretty simple, it manages only two buffers (the front and + * back buffer). + * + */ + +FramebufferNativeWindow::FramebufferNativeWindow() + : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false) +{ + hw_module_t const* module; + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { + int stride; + int err; + err = framebuffer_open(module, &fbDev); + LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err)); + + err = gralloc_open(module, &grDev); + LOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err)); + + // bail out if we can't initialize the modules + if (!fbDev || !grDev) + return; + + mUpdateOnDemand = (fbDev->setUpdateRect != 0); + + // initialize the buffer FIFO + mNumBuffers = 2; + mNumFreeBuffers = 2; + mBufferHead = mNumBuffers-1; + buffers[0] = new NativeBuffer( + fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); + buffers[1] = new NativeBuffer( + fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); + + err = grDev->alloc(grDev, + fbDev->width, fbDev->height, fbDev->format, + GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride); + + LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s", + fbDev->width, fbDev->height, strerror(-err)); + + err = grDev->alloc(grDev, + fbDev->width, fbDev->height, fbDev->format, + GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride); + + LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", + fbDev->width, fbDev->height, strerror(-err)); + + LOGE("xDpi %d", fbDev->xdpi); + LOGE("yDpi %d", fbDev->ydpi); + const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; + const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi; + const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi; + const_cast<int&>(android_native_window_t::minSwapInterval) = + fbDev->minSwapInterval; + const_cast<int&>(android_native_window_t::maxSwapInterval) = + fbDev->maxSwapInterval; + } else { + LOGE("Couldn't get gralloc module"); + } + + android_native_window_t::setSwapInterval = setSwapInterval; + android_native_window_t::dequeueBuffer = dequeueBuffer; + android_native_window_t::lockBuffer = lockBuffer; + android_native_window_t::queueBuffer = queueBuffer; + android_native_window_t::query = query; + android_native_window_t::perform = perform; +} + +FramebufferNativeWindow::~FramebufferNativeWindow() +{ + if (grDev) { + if (buffers[0] != NULL) + grDev->free(grDev, buffers[0]->handle); + if (buffers[1] != NULL) + grDev->free(grDev, buffers[1]->handle); + gralloc_close(grDev); + } + + if (fbDev) { + framebuffer_close(fbDev); + } +} + +status_t FramebufferNativeWindow::setUpdateRectangle(const Rect& r) +{ + if (!mUpdateOnDemand) { + return INVALID_OPERATION; + } + return fbDev->setUpdateRect(fbDev, r.left, r.top, r.width(), r.height()); +} + +status_t FramebufferNativeWindow::compositionComplete() +{ + if (fbDev->compositionComplete) { + return fbDev->compositionComplete(fbDev); + } + return INVALID_OPERATION; +} + +int FramebufferNativeWindow::setSwapInterval( + android_native_window_t* window, int interval) +{ + framebuffer_device_t* fb = getSelf(window)->fbDev; + return fb->setSwapInterval(fb, interval); +} + +int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, + android_native_buffer_t** buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + framebuffer_device_t* fb = self->fbDev; + + // wait for a free buffer + while (!self->mNumFreeBuffers) { + self->mCondition.wait(self->mutex); + } + // get this buffer + self->mNumFreeBuffers--; + int index = self->mBufferHead++; + if (self->mBufferHead >= self->mNumBuffers) + self->mBufferHead = 0; + + *buffer = self->buffers[index].get(); + + return 0; +} + +int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + + // wait that the buffer we're locking is not front anymore + while (self->front == buffer) { + self->mCondition.wait(self->mutex); + } + + return NO_ERROR; +} + +int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + framebuffer_device_t* fb = self->fbDev; + buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; + int res = fb->post(fb, handle); + self->front = static_cast<NativeBuffer*>(buffer); + self->mNumFreeBuffers++; + self->mCondition.broadcast(); + return res; +} + +int FramebufferNativeWindow::query(android_native_window_t* window, + int what, int* value) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + framebuffer_device_t* fb = self->fbDev; + switch (what) { + case NATIVE_WINDOW_WIDTH: + *value = fb->width; + return NO_ERROR; + case NATIVE_WINDOW_HEIGHT: + *value = fb->height; + return NO_ERROR; + case NATIVE_WINDOW_FORMAT: + *value = fb->format; + return NO_ERROR; + } + *value = 0; + return BAD_VALUE; +} + +int FramebufferNativeWindow::perform(android_native_window_t* window, + int operation, ...) +{ + switch (operation) { + case NATIVE_WINDOW_SET_USAGE: + break; + default: + return NAME_NOT_FOUND; + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +EGLNativeWindowType android_createDisplaySurface(void) +{ + FramebufferNativeWindow* w; + w = new FramebufferNativeWindow(); + if (w->getDevice() == NULL) { + // get a ref so it can be destroyed when we exit this block + sp<FramebufferNativeWindow> ref(w); + return NULL; + } + return (EGLNativeWindowType)w; +} diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp new file mode 100644 index 0000000..6a5c8a9 --- /dev/null +++ b/libs/ui/GraphicBuffer.cpp @@ -0,0 +1,239 @@ +/* + * 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 <binder/Parcel.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferAllocator.h> +#include <ui/GraphicBufferMapper.h> +#include <ui/PixelFormat.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +// =========================================================================== +// Buffer and implementation of android_native_buffer_t +// =========================================================================== + +GraphicBuffer::GraphicBuffer() + : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) +{ + width = + height = + stride = + format = + usage = 0; + handle = NULL; +} + +GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, + PixelFormat reqFormat, uint32_t reqUsage) + : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) +{ + width = + height = + stride = + format = + usage = 0; + handle = NULL; + mInitCheck = initSize(w, h, reqFormat, reqUsage); +} + +GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, + PixelFormat inFormat, uint32_t inUsage, + uint32_t inStride, native_handle_t* inHandle, bool keepOwnership) + : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), + mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) +{ + width = w; + height = h; + stride = inStride; + format = inFormat; + usage = inUsage; + handle = inHandle; +} + +GraphicBuffer::GraphicBuffer(const Parcel& data) + : BASE(), mOwner(ownHandle), mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mVStride(0), mIndex(-1) +{ + // we own the handle in this case + width = data.readInt32(); + if (width < 0) { + width = height = stride = format = usage = 0; + handle = 0; + } else { + height = data.readInt32(); + stride = data.readInt32(); + format = data.readInt32(); + usage = data.readInt32(); + handle = data.readNativeHandle(); + } +} + +GraphicBuffer::~GraphicBuffer() +{ + if (handle) { + if (mOwner == ownHandle) { + native_handle_close(handle); + native_handle_delete(const_cast<native_handle*>(handle)); + } else if (mOwner == ownData) { + GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); + allocator.free(handle); + } + } +} + +status_t GraphicBuffer::initCheck() const { + return mInitCheck; +} + +android_native_buffer_t* GraphicBuffer::getNativeBuffer() const +{ + return static_cast<android_native_buffer_t*>( + const_cast<GraphicBuffer*>(this)); +} + +status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f, + uint32_t reqUsage) +{ + if (mOwner != ownData) + return INVALID_OPERATION; + + if (handle) { + GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); + allocator.free(handle); + handle = 0; + } + return initSize(w, h, f, reqUsage); +} + +status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format, + uint32_t reqUsage) +{ + if (format == PIXEL_FORMAT_RGBX_8888) + format = PIXEL_FORMAT_RGBA_8888; + + GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); + status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride); + if (err == NO_ERROR) { + this->width = w; + this->height = h; + this->format = format; + this->usage = reqUsage; + mVStride = 0; + } + return err; +} + +status_t GraphicBuffer::lock(uint32_t usage, void** vaddr) +{ + const Rect lockBounds(width, height); + status_t res = lock(usage, lockBounds, vaddr); + return res; +} + +status_t GraphicBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr) +{ + if (rect.left < 0 || rect.right > this->width || + rect.top < 0 || rect.bottom > this->height) { + LOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", + rect.left, rect.top, rect.right, rect.bottom, + this->width, this->height); + return BAD_VALUE; + } + status_t res = getBufferMapper().lock(handle, usage, rect, vaddr); + return res; +} + +status_t GraphicBuffer::unlock() +{ + status_t res = getBufferMapper().unlock(handle); + return res; +} + +status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) +{ + void* vaddr; + status_t res = GraphicBuffer::lock(usage, &vaddr); + if (res == NO_ERROR && sur) { + sur->version = sizeof(GGLSurface); + sur->width = width; + sur->height = height; + sur->stride = stride; + sur->format = format; + sur->vstride = mVStride; + sur->data = static_cast<GGLubyte*>(vaddr); + } + return res; +} + + +status_t GraphicBuffer::writeToParcel(Parcel* reply, + android_native_buffer_t const* buffer) +{ + if (buffer == NULL) + return BAD_VALUE; + + if (buffer->width < 0 || buffer->height < 0) + return BAD_VALUE; + + status_t err = NO_ERROR; + if (buffer->handle == NULL) { + // this buffer doesn't have a handle + reply->writeInt32(NO_MEMORY); + } else { + reply->writeInt32(buffer->width); + reply->writeInt32(buffer->height); + reply->writeInt32(buffer->stride); + reply->writeInt32(buffer->format); + reply->writeInt32(buffer->usage); + err = reply->writeNativeHandle(buffer->handle); + } + return err; +} + + +void GraphicBuffer::setIndex(int index) { + mIndex = index; +} + +int GraphicBuffer::getIndex() const { + return mIndex; +} + +void GraphicBuffer::setVerticalStride(uint32_t vstride) { + mVStride = vstride; +} + +uint32_t GraphicBuffer::getVerticalStride() const { + return mVStride; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp new file mode 100644 index 0000000..57d5fc3 --- /dev/null +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -0,0 +1,141 @@ +/* +** +** Copyright 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 <cutils/log.h> + +#include <utils/Singleton.h> +#include <utils/String8.h> + +#include <ui/GraphicBufferAllocator.h> + +#include <private/ui/sw_gralloc_handle.h> + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) + +Mutex GraphicBufferAllocator::sLock; +KeyedVector<buffer_handle_t, + GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; + +GraphicBufferAllocator::GraphicBufferAllocator() + : mAllocDev(0) +{ + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); + if (err == 0) { + gralloc_open(module, &mAllocDev); + } +} + +GraphicBufferAllocator::~GraphicBufferAllocator() +{ + gralloc_close(mAllocDev); +} + +void GraphicBufferAllocator::dump(String8& result) const +{ + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + size_t total = 0; + const size_t SIZE = 512; + char buffer[SIZE]; + snprintf(buffer, SIZE, "Allocated buffers:\n"); + result.append(buffer); + const size_t c = list.size(); + for (size_t i=0 ; i<c ; i++) { + const alloc_rec_t& rec(list.valueAt(i)); + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n", + list.keyAt(i), rec.size/1024.0f, + rec.w, rec.h, rec.format, rec.usage); + result.append(buffer); + total += rec.size; + } + snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f); + result.append(buffer); +} + +static inline uint32_t clamp(uint32_t c) { + return c>0 ? c : 1; +} + +status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, + int usage, buffer_handle_t* handle, int32_t* stride) +{ + Mutex::Autolock _l(mLock); + + // make sure to not allocate a 0 x 0 buffer + w = clamp(w); + h = clamp(h); + + // we have a h/w allocator and h/w buffer is requested + status_t err; + + if (usage & GRALLOC_USAGE_HW_MASK) { + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); + } else { + err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride); + } + + LOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", + w, h, format, usage, err, strerror(-err)); + + if (err == NO_ERROR) { + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.w = w; + rec.h = h; + rec.format = format; + rec.usage = usage; + rec.vaddr = 0; + rec.size = h * stride[0] * bytesPerPixel(format); + list.add(*handle, rec); + } else { + String8 s; + dump(s); + LOGD("%s", s.string()); + } + + return err; +} + +status_t GraphicBufferAllocator::free(buffer_handle_t handle) +{ + Mutex::Autolock _l(mLock); + + status_t err; + if (sw_gralloc_handle_t::validate(handle) < 0) { + err = mAllocDev->free(mAllocDev, handle); + } else { + err = sw_gralloc_handle_t::free((sw_gralloc_handle_t*)handle); + } + + LOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); + if (err == NO_ERROR) { + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + list.removeItem(handle); + } + + return err; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp new file mode 100644 index 0000000..ce2acd0 --- /dev/null +++ b/libs/ui/GraphicBufferMapper.cpp @@ -0,0 +1,228 @@ +/* + * 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 "GraphicBufferMapper" + +#include <stdint.h> +#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define +# include <asm/page.h> +#else +# include <sys/user.h> +#endif +#include <errno.h> +#include <sys/mman.h> + +#include <cutils/ashmem.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBufferMapper.h> +#include <ui/Rect.h> + +#include <hardware/gralloc.h> + +#include <private/ui/sw_gralloc_handle.h> + + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) + +GraphicBufferMapper::GraphicBufferMapper() + : mAllocMod(0) +{ + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); + if (err == 0) { + mAllocMod = (gralloc_module_t const *)module; + } +} + +status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) +{ + status_t err; + if (sw_gralloc_handle_t::validate(handle) < 0) { + err = mAllocMod->registerBuffer(mAllocMod, handle); + } else { + err = sw_gralloc_handle_t::registerBuffer((sw_gralloc_handle_t*)handle); + } + LOGW_IF(err, "registerBuffer(%p) failed %d (%s)", + handle, err, strerror(-err)); + return err; +} + +status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) +{ + status_t err; + if (sw_gralloc_handle_t::validate(handle) < 0) { + err = mAllocMod->unregisterBuffer(mAllocMod, handle); + } else { + err = sw_gralloc_handle_t::unregisterBuffer((sw_gralloc_handle_t*)handle); + } + LOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)", + handle, err, strerror(-err)); + return err; +} + +status_t GraphicBufferMapper::lock(buffer_handle_t handle, + int usage, const Rect& bounds, void** vaddr) +{ + status_t err; + if (sw_gralloc_handle_t::validate(handle) < 0) { + err = mAllocMod->lock(mAllocMod, handle, usage, + bounds.left, bounds.top, bounds.width(), bounds.height(), + vaddr); + } else { + err = sw_gralloc_handle_t::lock((sw_gralloc_handle_t*)handle, usage, + bounds.left, bounds.top, bounds.width(), bounds.height(), + vaddr); + } + LOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); + return err; +} + +status_t GraphicBufferMapper::unlock(buffer_handle_t handle) +{ + status_t err; + if (sw_gralloc_handle_t::validate(handle) < 0) { + err = mAllocMod->unlock(mAllocMod, handle); + } else { + err = sw_gralloc_handle_t::unlock((sw_gralloc_handle_t*)handle); + } + LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); + return err; +} + +// --------------------------------------------------------------------------- + +status_t sw_gralloc_handle_t::alloc(uint32_t w, uint32_t h, int format, + int usage, buffer_handle_t* pHandle, int32_t* pStride) +{ + int align = 4; + int bpp = 0; + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + bpp = 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + default: + return -EINVAL; + } + size_t bpr = (w*bpp + (align-1)) & ~(align-1); + size_t size = bpr * h; + size_t stride = bpr / bpp; + size = (size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); + + int fd = ashmem_create_region("sw-gralloc-buffer", size); + if (fd < 0) { + LOGE("ashmem_create_region(size=%d) failed (%s)", + size, strerror(-errno)); + return -errno; + } + + int prot = PROT_READ; + if (usage & GRALLOC_USAGE_SW_WRITE_MASK) + prot |= PROT_WRITE; + + if (ashmem_set_prot_region(fd, prot) < 0) { + LOGE("ashmem_set_prot_region(fd=%d, prot=%x) failed (%s)", + fd, prot, strerror(-errno)); + close(fd); + return -errno; + } + + void* base = mmap(0, size, prot, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("alloc mmap(fd=%d, size=%d, prot=%x) failed (%s)", + fd, size, prot, strerror(-errno)); + close(fd); + return -errno; + } + + sw_gralloc_handle_t* hnd = new sw_gralloc_handle_t(); + hnd->fd = fd; + hnd->size = size; + hnd->base = intptr_t(base); + hnd->prot = prot; + *pStride = stride; + *pHandle = hnd; + + return NO_ERROR; +} + +status_t sw_gralloc_handle_t::free(sw_gralloc_handle_t* hnd) +{ + if (hnd->base) { + munmap((void*)hnd->base, hnd->size); + } + if (hnd->fd >= 0) { + close(hnd->fd); + } + delete hnd; + return NO_ERROR; +} + +status_t sw_gralloc_handle_t::registerBuffer(sw_gralloc_handle_t* hnd) +{ + if (hnd->pid != getpid()) { + void* base = mmap(0, hnd->size, hnd->prot, MAP_SHARED, hnd->fd, 0); + if (base == MAP_FAILED) { + LOGE("registerBuffer mmap(fd=%d, size=%d, prot=%x) failed (%s)", + hnd->fd, hnd->size, hnd->prot, strerror(-errno)); + return -errno; + } + hnd->base = intptr_t(base); + } + return NO_ERROR; +} + +status_t sw_gralloc_handle_t::unregisterBuffer(sw_gralloc_handle_t* hnd) +{ + if (hnd->pid != getpid()) { + if (hnd->base) { + munmap((void*)hnd->base, hnd->size); + } + hnd->base = 0; + } + return NO_ERROR; +} + +status_t sw_gralloc_handle_t::lock(sw_gralloc_handle_t* hnd, int usage, + int l, int t, int w, int h, void** vaddr) +{ + *vaddr = (void*)hnd->base; + return NO_ERROR; +} + +status_t sw_gralloc_handle_t::unlock(sw_gralloc_handle_t* hnd) +{ + return NO_ERROR; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp index ab0fef1..e1b3ec7 100644 --- a/libs/ui/ICamera.cpp +++ b/libs/ui/ICamera.cpp @@ -20,7 +20,7 @@ #include <utils/Log.h> #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <ui/ICamera.h> namespace android { @@ -32,9 +32,11 @@ enum { START_PREVIEW, STOP_PREVIEW, AUTO_FOCUS, + CANCEL_AUTO_FOCUS, TAKE_PICTURE, SET_PARAMETERS, GET_PARAMETERS, + SEND_COMMAND, CONNECT, LOCK, UNLOCK, @@ -162,6 +164,17 @@ public: return ret; } + // cancel focus + status_t cancelAutoFocus() + { + LOGV("cancelAutoFocus"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(CANCEL_AUTO_FOCUS, data, &reply); + status_t ret = reply.readInt32(); + return ret; + } + // take a picture - returns an IMemory (ref-counted mmap) status_t takePicture() { @@ -193,6 +206,17 @@ public: remote()->transact(GET_PARAMETERS, data, &reply); return reply.readString8(); } + virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) + { + LOGD("sendCommand"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(cmd); + data.writeInt32(arg1); + data.writeInt32(arg2); + remote()->transact(SEND_COMMAND, data, &reply); + return reply.readInt32(); + } virtual status_t connect(const sp<ICameraClient>& cameraClient) { Parcel data, reply; @@ -221,12 +245,6 @@ IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCamera::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -300,6 +318,12 @@ status_t BnCamera::onTransact( reply->writeInt32(autoFocus()); return NO_ERROR; } break; + case CANCEL_AUTO_FOCUS: { + LOGV("CANCEL_AUTO_FOCUS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(cancelAutoFocus()); + return NO_ERROR; + } break; case TAKE_PICTURE: { LOGV("TAKE_PICTURE"); CHECK_INTERFACE(ICamera, data, reply); @@ -319,6 +343,15 @@ status_t BnCamera::onTransact( reply->writeString8(getParameters()); return NO_ERROR; } break; + case SEND_COMMAND: { + LOGD("SEND_COMMAND"); + CHECK_INTERFACE(ICamera, data, reply); + int command = data.readInt32(); + int arg1 = data.readInt32(); + int arg2 = data.readInt32(); + reply->writeInt32(sendCommand(command, arg1, arg2)); + return NO_ERROR; + } break; case CONNECT: { CHECK_INTERFACE(ICamera, data, reply); sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder()); diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp index 59a6cf2..42b4da4 100644 --- a/libs/ui/ICameraClient.cpp +++ b/libs/ui/ICameraClient.cpp @@ -78,12 +78,6 @@ IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCameraClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp index e5687fe..84986c6 100644 --- a/libs/ui/ICameraService.cpp +++ b/libs/ui/ICameraService.cpp @@ -18,9 +18,9 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ICameraService.h> @@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCameraService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp index fed47c2..65e6b4f 100644 --- a/libs/ui/IOverlay.cpp +++ b/libs/ui/IOverlay.cpp @@ -18,8 +18,8 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IInterface.h> +#include <binder/Parcel.h> +#include <binder/IInterface.h> #include <ui/IOverlay.h> @@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnOverlay::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp index d5e9f81..4fb38ed 100644 --- a/libs/ui/ISurface.cpp +++ b/libs/ui/ISurface.cpp @@ -14,19 +14,25 @@ * limitations under the License. */ +#define LOG_TAG "ISurface" + #include <stdio.h> #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> #include <ui/ISurface.h> #include <ui/Overlay.h> +#include <ui/Surface.h> +#include <ui/GraphicBuffer.h> namespace android { +// ---------------------------------------------------------------------- + ISurface::BufferHeap::BufferHeap() : w(0), h(0), hor_stride(0), ver_stride(0), format(0), transform(0), flags(0) @@ -55,6 +61,8 @@ ISurface::BufferHeap::~BufferHeap() { } +// ---------------------------------------------------------------------- + class BpSurface : public BpInterface<ISurface> { public: @@ -63,6 +71,17 @@ public: { } + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferIdx); + data.writeInt32(usage); + remote()->transact(REQUEST_BUFFER, data, &reply); + sp<GraphicBuffer> buffer = new GraphicBuffer(reply); + return buffer; + } + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; @@ -112,16 +131,17 @@ IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurface::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { + case REQUEST_BUFFER: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferIdx = data.readInt32(); + int usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage)); + return GraphicBuffer::writeToParcel(reply, buffer.get()); + } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); BufferHeap buffer; diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp index 76597e1..fd2a590 100644 --- a/libs/ui/ISurfaceComposer.cpp +++ b/libs/ui/ISurfaceComposer.cpp @@ -20,10 +20,10 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ISurfaceComposer.h> #include <ui/DisplayInfo.h> @@ -54,12 +54,12 @@ public: return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder()); } - virtual sp<IMemory> getCblk() const + virtual sp<IMemoryHeap> getCblk() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); - return interface_cast<IMemory>(reply.readStrongBinder()); + return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } virtual void openGlobalTransaction() @@ -114,36 +114,6 @@ public: remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } - virtual status_t requestGPU( - const sp<IGPUCallback>& callback, gpu_info_t* gpu) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(callback->asBinder()); - remote()->transact(BnSurfaceComposer::REQUEST_GPU, data, &reply); - gpu->regs = interface_cast<IMemory>(reply.readStrongBinder()); - gpu->count = reply.readInt32(); - - // FIXME: for now, we don't dynamically allocate the regions array - size_t maxCount = sizeof(gpu->regions)/sizeof(*gpu->regions); - if (gpu->count > maxCount) - return BAD_VALUE; - - for (size_t i=0 ; i<gpu->count ; i++) { - gpu->regions[i].region = interface_cast<IMemory>(reply.readStrongBinder()); - gpu->regions[i].reserved = reply.readInt32(); - } - return reply.readInt32(); - } - - virtual status_t revokeGPU() - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::REVOKE_GPU, data, &reply); - return reply.readInt32(); - } - virtual void signal() const { Parcel data, reply; @@ -156,124 +126,61 @@ IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - status_t err = BnInterface<ISurfaceComposer>::onTransact(code, data, reply, flags); - if (err == NO_ERROR) - return err; - - CHECK_INTERFACE(ISurfaceComposer, data, reply); - switch(code) { case CREATE_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); } break; case OPEN_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); openGlobalTransaction(); } break; case CLOSE_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); closeGlobalTransaction(); } break; case SET_ORIENTATION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); int orientation = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( setOrientation(dpy, orientation, flags) ); } break; case FREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( freezeDisplay(dpy, flags) ); } break; case UNFREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( unfreezeDisplay(dpy, flags) ); } break; case BOOT_FINISHED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case REVOKE_GPU: { - reply->writeInt32( revokeGPU() ); - } break; case SIGNAL: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); signal(); } break; case GET_CBLK: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = getCblk()->asBinder(); reply->writeStrongBinder(b); } break; - case REQUEST_GPU: { - // TODO: this should be protected by a permission - gpu_info_t info; - sp<IGPUCallback> callback - = interface_cast<IGPUCallback>(data.readStrongBinder()); - status_t res = requestGPU(callback, &info); - - // FIXME: for now, we don't dynamically allocate the regions array - size_t maxCount = sizeof(info.regions)/sizeof(*info.regions); - if (info.count > maxCount) - return BAD_VALUE; - - reply->writeStrongBinder(info.regs->asBinder()); - reply->writeInt32(info.count); - for (size_t i=0 ; i<info.count ; i++) { - reply->writeStrongBinder(info.regions[i].region->asBinder()); - reply->writeInt32(info.regions[i].reserved); - } - reply->writeInt32(res); - } break; default: - return UNKNOWN_TRANSACTION; + return BBinder::onTransact(code, data, reply, flags); } return NO_ERROR; } // ---------------------------------------------------------------------------- -enum { - // Note: BOOT_FINISHED must remain this value, it is called by ActivityManagerService. - GPU_LOST = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpGPUCallback : public BpInterface<IGPUCallback> -{ -public: - BpGPUCallback(const sp<IBinder>& impl) - : BpInterface<IGPUCallback>(impl) - { - } - - virtual void gpuLost() - { - Parcel data, reply; - data.writeInterfaceToken(IGPUCallback::getInterfaceDescriptor()); - remote()->transact(GPU_LOST, data, &reply, IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(GPUCallback, "android.ui.IGPUCallback"); - -status_t BnGPUCallback::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case GPU_LOST: { - CHECK_INTERFACE(IGPUCallback, data, reply); - gpuLost(); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - }; diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp index dab5f71..4a6a1d7 100644 --- a/libs/ui/ISurfaceFlingerClient.cpp +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -21,10 +21,10 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ISurface.h> #include <ui/ISurfaceFlingerClient.h> @@ -64,12 +64,12 @@ public: { } - virtual void getControlBlocks(sp<IMemory>* ctl) const + virtual sp<IMemoryHeap> getControlBlock() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); remote()->transact(GET_CBLK, data, &reply); - *ctl = interface_cast<IMemory>(reply.readStrongBinder()); + return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } virtual sp<ISurface> createSurface( surface_data_t* params, @@ -118,12 +118,6 @@ IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurfaceFlingerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -132,8 +126,7 @@ status_t BnSurfaceFlingerClient::onTransact( switch(code) { case GET_CBLK: { CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); - sp<IMemory> ctl; - getControlBlocks(&ctl); + sp<IMemoryHeap> ctl(getControlBlock()); reply->writeStrongBinder(ctl->asBinder()); return NO_ERROR; } break; @@ -196,10 +189,11 @@ status_t BnSurfaceFlingerClient::onTransact( status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel) { - token = parcel.readInt32(); - identity = parcel.readInt32(); - heap[0] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); - heap[1] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); + token = parcel.readInt32(); + identity = parcel.readInt32(); + width = parcel.readInt32(); + height = parcel.readInt32(); + format = parcel.readInt32(); return NO_ERROR; } @@ -207,8 +201,9 @@ status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) co { parcel->writeInt32(token); parcel->writeInt32(identity); - parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); - parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); return NO_ERROR; } diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp index 0b6374b..a53ffb7 100644 --- a/libs/ui/LayerState.cpp +++ b/libs/ui/LayerState.cpp @@ -15,7 +15,7 @@ */ #include <utils/Errors.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <private/ui/LayerState.h> namespace android { diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index 59c6514..3aa8950 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include <utils/IMemory.h> -#include <utils/Parcel.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> #include <utils/Errors.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapBase.h> #include <ui/IOverlay.h> #include <ui/Overlay.h> @@ -59,6 +59,30 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer) return mOverlayData->queueBuffer(mOverlayData, buffer); } +status_t Overlay::resizeInput(uint32_t width, uint32_t height) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->resizeInput(mOverlayData, width, height); +} + +status_t Overlay::setParameter(int param, int value) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->setParameter(mOverlayData, param, value); +} + +status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->setCrop(mOverlayData, x, y, w, h); +} + +status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getCrop(mOverlayData, x, y, w, h); +} + int32_t Overlay::getBufferCount() const { if (mStatus != NO_ERROR) return mStatus; @@ -73,6 +97,15 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer) void Overlay::destroy() { if (mStatus != NO_ERROR) return; + + // Must delete the objects in reverse creation order, thus the + // data side must be closed first and then the destroy send to + // the control side. + if (mOverlayData) { + overlay_data_close(mOverlayData); + mOverlayData = NULL; + } + mOverlayRef->mOverlayChannel->destroy(); } diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 26e694a..d21ed57 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -16,295 +16,661 @@ #define LOG_TAG "Region" -#include <stdio.h> -#include <utils/Atomic.h> -#include <utils/Debug.h> +#include <limits.h> + +#include <utils/Log.h> #include <utils/String8.h> + +#include <ui/Rect.h> #include <ui/Region.h> +#include <ui/Point.h> + +#include <private/ui/RegionHelper.h> + +// ---------------------------------------------------------------------------- +#define VALIDATE_REGIONS (false) +#define VALIDATE_WITH_CORECG (false) +// ---------------------------------------------------------------------------- + +#if VALIDATE_WITH_CORECG +#include <core/SkRegion.h> +#endif namespace android { +// ---------------------------------------------------------------------------- + +enum { + op_nand = region_operator<Rect>::op_nand, + op_and = region_operator<Rect>::op_and, + op_or = region_operator<Rect>::op_or, + op_xor = region_operator<Rect>::op_xor +}; // ---------------------------------------------------------------------------- Region::Region() + : mBounds(0,0) { } Region::Region(const Region& rhs) - : mRegion(rhs.mRegion) -{ -} - -Region::Region(const SkRegion& rhs) - : mRegion(rhs) -{ -} - -Region::~Region() + : mBounds(rhs.mBounds), mStorage(rhs.mStorage) { } Region::Region(const Rect& rhs) + : mBounds(rhs) { - set(rhs); } Region::Region(const Parcel& parcel) { - read(parcel); + status_t err = read(parcel); + LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err)); } Region::Region(const void* buffer) { - read(buffer); + status_t err = read(buffer); + LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err)); } -Region& Region::operator = (const Region& rhs) +Region::~Region() { - mRegion = rhs.mRegion; - return *this; } -const SkRegion& Region::toSkRegion() const +Region& Region::operator = (const Region& rhs) { - return mRegion; +#if VALIDATE_REGIONS + validate(rhs, "operator="); +#endif + mBounds = rhs.mBounds; + mStorage = rhs.mStorage; + return *this; } -Rect Region::bounds() const +Region& Region::makeBoundsSelf() { - const SkIRect& b(mRegion.getBounds()); - return Rect(b.fLeft, b.fTop, b.fRight, b.fBottom); + mStorage.clear(); + return *this; } void Region::clear() { - mRegion.setEmpty(); + mBounds.clear(); + mStorage.clear(); } void Region::set(const Rect& r) { - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.setRect(ir); + mBounds = r; + mStorage.clear(); +} + +void Region::set(uint32_t w, uint32_t h) +{ + mBounds = Rect(int(w), int(h)); + mStorage.clear(); } // ---------------------------------------------------------------------------- -Region& Region::orSelf(const Rect& r) +void Region::addRectUnchecked(int l, int t, int r, int b) { - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.op(ir, SkRegion::kUnion_Op); - return *this; + mStorage.add(Rect(l,t,r,b)); +#if VALIDATE_REGIONS + validate(*this, "addRectUnchecked"); +#endif } -Region& Region::andSelf(const Rect& r) -{ - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.op(ir, SkRegion::kIntersect_Op); +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Rect& r) { + return operationSelf(r, op_or); +} +Region& Region::andSelf(const Rect& r) { + return operationSelf(r, op_and); +} +Region& Region::subtractSelf(const Rect& r) { + return operationSelf(r, op_nand); +} +Region& Region::operationSelf(const Rect& r, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, r); return *this; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kUnion_Op); - return *this; + return operationSelf(rhs, op_or); } - Region& Region::andSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kIntersect_Op); - return *this; + return operationSelf(rhs, op_and); } - Region& Region::subtractSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kDifference_Op); + return operationSelf(rhs, op_nand); +} +Region& Region::operationSelf(const Region& rhs, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, rhs); return *this; } Region& Region::translateSelf(int x, int y) { - if (x|y) mRegion.translate(x, y); + if (x|y) translate(*this, x, y); return *this; } -Region Region::merge(const Region& rhs) const { - Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kUnion_Op); - return result; -} +// ---------------------------------------------------------------------------- -Region Region::intersect(const Region& rhs) const { +const Region Region::merge(const Rect& rhs) const { + return operation(rhs, op_or); +} +const Region Region::intersect(const Rect& rhs) const { + return operation(rhs, op_and); +} +const Region Region::subtract(const Rect& rhs) const { + return operation(rhs, op_nand); +} +const Region Region::operation(const Rect& rhs, int op) const { Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kIntersect_Op); + boolean_operation(op, result, *this, rhs); return result; } -Region Region::subtract(const Region& rhs) const { +// ---------------------------------------------------------------------------- + +const Region Region::merge(const Region& rhs) const { + return operation(rhs, op_or); +} +const Region Region::intersect(const Region& rhs) const { + return operation(rhs, op_and); +} +const Region Region::subtract(const Region& rhs) const { + return operation(rhs, op_nand); +} +const Region Region::operation(const Region& rhs, int op) const { Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kDifference_Op); + boolean_operation(op, result, *this, rhs); return result; } -Region Region::translate(int x, int y) const { +const Region Region::translate(int x, int y) const { Region result; - mRegion.translate(x, y, &result.mRegion); + translate(result, *this, x, y); return result; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kUnion_Op); - return *this; + return operationSelf(rhs, dx, dy, op_or); } - Region& Region::andSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kIntersect_Op); - return *this; + return operationSelf(rhs, dx, dy, op_and); } - Region& Region::subtractSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kDifference_Op); + return operationSelf(rhs, dx, dy, op_nand); +} +Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, rhs, dx, dy); return *this; } -Region Region::merge(const Region& rhs, int dx, int dy) const { +// ---------------------------------------------------------------------------- + +const Region Region::merge(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_or); +} +const Region Region::intersect(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_and); +} +const Region Region::subtract(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_nand); +} +const Region Region::operation(const Region& rhs, int dx, int dy, int op) const { Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kUnion_Op); + boolean_operation(op, result, *this, rhs, dx, dy); return result; } -Region Region::intersect(const Region& rhs, int dx, int dy) const { - Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kIntersect_Op); +// ---------------------------------------------------------------------------- + +// This is our region rasterizer, which merges rects and spans together +// to obtain an optimal region. +class Region::rasterizer : public region_operator<Rect>::region_rasterizer +{ + Rect& bounds; + Vector<Rect>& storage; + Rect* head; + Rect* tail; + Vector<Rect> span; + Rect* cur; +public: + rasterizer(Region& reg) + : bounds(reg.mBounds), storage(reg.mStorage), head(), tail(), cur() { + bounds.top = bounds.bottom = 0; + bounds.left = INT_MAX; + bounds.right = INT_MIN; + storage.clear(); + } + + ~rasterizer() { + if (span.size()) { + flushSpan(); + } + if (storage.size()) { + bounds.top = storage.itemAt(0).top; + bounds.bottom = storage.top().bottom; + if (storage.size() == 1) { + storage.clear(); + } + } else { + bounds.left = 0; + bounds.right = 0; + } + } + + virtual void operator()(const Rect& rect) { + //LOGD(">>> %3d, %3d, %3d, %3d", + // rect.left, rect.top, rect.right, rect.bottom); + if (span.size()) { + if (cur->top != rect.top) { + flushSpan(); + } else if (cur->right == rect.left) { + cur->right = rect.right; + return; + } + } + span.add(rect); + cur = span.editArray() + (span.size() - 1); + } +private: + template<typename T> + static inline T min(T rhs, T lhs) { return rhs < lhs ? rhs : lhs; } + template<typename T> + static inline T max(T rhs, T lhs) { return rhs > lhs ? rhs : lhs; } + void flushSpan() { + bool merge = false; + if (tail-head == ssize_t(span.size())) { + Rect const* p = cur; + Rect const* q = head; + if (p->top == q->bottom) { + merge = true; + while (q != tail) { + if ((p->left != q->left) || (p->right != q->right)) { + merge = false; + break; + } + p++, q++; + } + } + } + if (merge) { + const int bottom = span[0].bottom; + Rect* r = head; + while (r != tail) { + r->bottom = bottom; + r++; + } + } else { + bounds.left = min(span.itemAt(0).left, bounds.left); + bounds.right = max(span.top().right, bounds.right); + storage.appendVector(span); + tail = storage.editArray() + storage.size(); + head = tail - span.size(); + } + span.clear(); + } +}; + +bool Region::validate(const Region& reg, const char* name) +{ + bool result = true; + const_iterator cur = reg.begin(); + const_iterator const tail = reg.end(); + const_iterator prev = cur++; + Rect b(*prev); + while (cur != tail) { + b.left = b.left < cur->left ? b.left : cur->left; + b.top = b.top < cur->top ? b.top : cur->top; + b.right = b.right > cur->right ? b.right : cur->right; + b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom; + if (cur->top == prev->top) { + if (cur->bottom != prev->bottom) { + LOGE("%s: invalid span %p", name, cur); + result = false; + } else if (cur->left < prev->right) { + LOGE("%s: spans overlap horizontally prev=%p, cur=%p", + name, prev, cur); + result = false; + } + } else if (cur->top < prev->bottom) { + LOGE("%s: spans overlap vertically prev=%p, cur=%p", + name, prev, cur); + result = false; + } + prev = cur; + cur++; + } + if (b != reg.getBounds()) { + result = false; + LOGE("%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name, + b.left, b.top, b.right, b.bottom, + reg.getBounds().left, reg.getBounds().top, + reg.getBounds().right, reg.getBounds().bottom); + } + if (result == false) { + reg.dump(name); + } return result; } -Region Region::subtract(const Region& rhs, int dx, int dy) const { - Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kDifference_Op); - return result; +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, + const Region& rhs, int dx, int dy) +{ + size_t lhs_count; + Rect const * const lhs_rects = lhs.getArray(&lhs_count); + + size_t rhs_count; + Rect const * const rhs_rects = rhs.getArray(&rhs_count); + + region_operator<Rect>::region lhs_region(lhs_rects, lhs_count); + region_operator<Rect>::region rhs_region(rhs_rects, rhs_count, dx, dy); + region_operator<Rect> operation(op, lhs_region, rhs_region); + { // scope for rasterizer (dtor has side effects) + rasterizer r(dst); + operation(r); + } + +#if VALIDATE_REGIONS + validate(lhs, "boolean_operation: lhs"); + validate(rhs, "boolean_operation: rhs"); + validate(dst, "boolean_operation: dst"); +#endif + +#if VALIDATE_WITH_CORECG + SkRegion sk_lhs; + SkRegion sk_rhs; + SkRegion sk_dst; + + for (size_t i=0 ; i<lhs_count ; i++) + sk_lhs.op( + lhs_rects[i].left + dx, + lhs_rects[i].top + dy, + lhs_rects[i].right + dx, + lhs_rects[i].bottom + dy, + SkRegion::kUnion_Op); + + for (size_t i=0 ; i<rhs_count ; i++) + sk_rhs.op( + rhs_rects[i].left + dx, + rhs_rects[i].top + dy, + rhs_rects[i].right + dx, + rhs_rects[i].bottom + dy, + SkRegion::kUnion_Op); + + const char* name = "---"; + SkRegion::Op sk_op; + switch (op) { + case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break; + case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break; + case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break; + } + sk_dst.op(sk_lhs, sk_rhs, sk_op); + + if (sk_dst.isEmpty() && dst.isEmpty()) + return; + + bool same = true; + Region::const_iterator head = dst.begin(); + Region::const_iterator const tail = dst.end(); + SkRegion::Iterator it(sk_dst); + while (!it.done()) { + if (head != tail) { + if ( + head->left != it.rect().fLeft || + head->top != it.rect().fTop || + head->right != it.rect().fRight || + head->bottom != it.rect().fBottom + ) { + same = false; + break; + } + } else { + same = false; + break; + } + head++; + it.next(); + } + + if (head != tail) { + same = false; + } + + if(!same) { + LOGD("---\nregion boolean %s failed", name); + lhs.dump("lhs"); + rhs.dump("rhs"); + dst.dump("dst"); + LOGD("should be"); + SkRegion::Iterator it(sk_dst); + while (!it.done()) { + LOGD(" [%3d, %3d, %3d, %3d]", + it.rect().fLeft, + it.rect().fTop, + it.rect().fRight, + it.rect().fBottom); + it.next(); + } + } +#endif } -// ---------------------------------------------------------------------------- +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, + const Rect& rhs, int dx, int dy) +{ +#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS + boolean_operation(op, dst, lhs, Region(rhs), dx, dy); +#else + size_t lhs_count; + Rect const * const lhs_rects = lhs.getArray(&lhs_count); + + region_operator<Rect>::region lhs_region(lhs_rects, lhs_count); + region_operator<Rect>::region rhs_region(&rhs, 1, dx, dy); + region_operator<Rect> operation(op, lhs_region, rhs_region); + { // scope for rasterizer (dtor has side effects) + rasterizer r(dst); + operation(r); + } + +#endif +} -Region::iterator::iterator(const Region& r) - : mIt(r.mRegion) +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, const Region& rhs) { + boolean_operation(op, dst, lhs, rhs, 0, 0); } -int Region::iterator::iterate(Rect* rect) +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, const Rect& rhs) { - if (mIt.done()) - return 0; - const SkIRect& r(mIt.rect()); - rect->left = r.fLeft; - rect->top = r.fTop; - rect->right = r.fRight; - rect->bottom= r.fBottom; - mIt.next(); - return 1; + boolean_operation(op, dst, lhs, rhs, 0, 0); } -// ---------------------------------------------------------------------------- +void Region::translate(Region& reg, int dx, int dy) +{ + if (!reg.isEmpty()) { +#if VALIDATE_REGIONS + validate(reg, "translate (before)"); +#endif + reg.mBounds.translate(dx, dy); + size_t count = reg.mStorage.size(); + Rect* rects = reg.mStorage.editArray(); + while (count) { + rects->translate(dx, dy); + rects++; + count--; + } +#if VALIDATE_REGIONS + validate(reg, "translate (after)"); +#endif + } +} + +void Region::translate(Region& dst, const Region& reg, int dx, int dy) +{ + dst = reg; + translate(dst, dx, dy); +} -// we write a 4byte size ahead of the actual region, so we know how much we'll need for reading +// ---------------------------------------------------------------------------- status_t Region::write(Parcel& parcel) const { - int32_t size = mRegion.flatten(NULL); - parcel.writeInt32(size); - mRegion.flatten(parcel.writeInplace(size)); +#if VALIDATE_REGIONS + validate(*this, "write(Parcel)"); +#endif + status_t err; + const size_t count = mStorage.size(); + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); + void* buffer = parcel.writeInplace(sizeNeeded); + if (!buffer) return NO_MEMORY; + ssize_t written = Region::write(buffer, sizeNeeded); + if (written < 0) return status_t(written); return NO_ERROR; } status_t Region::read(const Parcel& parcel) { - size_t size = parcel.readInt32(); - mRegion.unflatten(parcel.readInplace(size)); + void const* buffer = parcel.readInplace(sizeof(int32_t)); + if (!buffer) return NO_MEMORY; + const size_t count = *static_cast<int32_t const *>(buffer); + void const* dummy = parcel.readInplace((1+count)*sizeof(Rect)); + if (!dummy) return NO_MEMORY; + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); + const ssize_t read = Region::read(buffer); + if (read < 0) return status_t(read); +#if VALIDATE_REGIONS + validate(*this, "read(Parcel)"); +#endif return NO_ERROR; } ssize_t Region::write(void* buffer, size_t size) const { - size_t sizeNeeded = mRegion.flatten(NULL); +#if VALIDATE_REGIONS + validate(*this, "write(buffer)"); +#endif + const size_t count = mStorage.size(); + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); if (sizeNeeded > size) return NO_MEMORY; - return mRegion.flatten(buffer); + int32_t* const p = static_cast<int32_t*>(buffer); + *p = count; + memcpy(p+1, &mBounds, sizeof(Rect)); + if (count) { + memcpy(p+5, mStorage.array(), count*sizeof(Rect)); + } + return ssize_t(sizeNeeded); } ssize_t Region::read(const void* buffer) { - return mRegion.unflatten(buffer); + int32_t const* const p = static_cast<int32_t const*>(buffer); + const size_t count = *p; + memcpy(&mBounds, p+1, sizeof(Rect)); + mStorage.clear(); + if (count) { + mStorage.insertAt(0, count); + memcpy(mStorage.editArray(), p+5, count*sizeof(Rect)); + } +#if VALIDATE_REGIONS + validate(*this, "read(buffer)"); +#endif + return ssize_t(sizeof(int32_t) + (1+count)*sizeof(Rect)); } ssize_t Region::writeEmpty(void* buffer, size_t size) { - if (size < 4) return NO_MEMORY; - // this needs to stay in sync with SkRegion - *static_cast<int32_t*>(buffer) = -1; - return 4; + const size_t sizeNeeded = sizeof(int32_t) + sizeof(Rect); + if (sizeNeeded > size) return NO_MEMORY; + int32_t* const p = static_cast<int32_t*>(buffer); + memset(p, 0, sizeNeeded); + return ssize_t(sizeNeeded); } bool Region::isEmpty(void* buffer) { - // this needs to stay in sync with SkRegion - return *static_cast<int32_t*>(buffer) == -1; + int32_t const* const p = static_cast<int32_t const*>(buffer); + Rect const* const b = reinterpret_cast<Rect const *>(p+1); + return b->isEmpty(); +} + +// ---------------------------------------------------------------------------- + +Region::const_iterator Region::begin() const { + return isRect() ? &mBounds : mStorage.array(); +} + +Region::const_iterator Region::end() const { + return isRect() ? ((&mBounds) + 1) : (mStorage.array() + mStorage.size()); +} + +Rect const* Region::getArray(size_t* count) const { + const_iterator const b(begin()); + const_iterator const e(end()); + if (count) *count = e-b; + return b; } -size_t Region::rects(Vector<Rect>& rectList) const +size_t Region::getRects(Vector<Rect>& rectList) const { - rectList.clear(); - if (!isEmpty()) { - SkRegion::Iterator iterator(mRegion); - while( !iterator.done() ) { - const SkIRect& ir(iterator.rect()); - rectList.push(Rect(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom)); - iterator.next(); - } + rectList = mStorage; + if (rectList.isEmpty()) { + rectList.clear(); + rectList.add(mBounds); } return rectList.size(); } +// ---------------------------------------------------------------------------- + void Region::dump(String8& out, const char* what, uint32_t flags) const { (void)flags; - Vector<Rect> r; - rects(r); - + const_iterator head = begin(); + const_iterator const tail = end(); + size_t SIZE = 256; char buffer[SIZE]; - - snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", what, this, r.size()); + + snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", + what, this, tail-head); out.append(buffer); - for (size_t i=0 ; i<r.size() ; i++) { + while (head != tail) { snprintf(buffer, SIZE, " [%3d, %3d, %3d, %3d]\n", - r[i].left, r[i].top,r[i].right,r[i].bottom); + head->left, head->top, head->right, head->bottom); out.append(buffer); + head++; } } void Region::dump(const char* what, uint32_t flags) const { (void)flags; - Vector<Rect> r; - rects(r); - LOGD(" Region %s (this=%p, count=%d)\n", what, this, r.size()); - for (size_t i=0 ; i<r.size() ; i++) { + const_iterator head = begin(); + const_iterator const tail = end(); + LOGD(" Region %s (this=%p, count=%d)\n", what, this, tail-head); + while (head != tail) { LOGD(" [%3d, %3d, %3d, %3d]\n", - r[i].left, r[i].top,r[i].right,r[i].bottom); + head->left, head->top, head->right, head->bottom); + head++; } } diff --git a/libs/ui/SharedBufferStack.cpp b/libs/ui/SharedBufferStack.cpp new file mode 100644 index 0000000..46b6766 --- /dev/null +++ b/libs/ui/SharedBufferStack.cpp @@ -0,0 +1,424 @@ +/* + * 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 "SharedBufferStack" + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Debug.h> +#include <utils/Log.h> +#include <utils/threads.h> + +#include <private/ui/SharedBufferStack.h> + +#include <ui/Rect.h> +#include <ui/Region.h> + +#define DEBUG_ATOMICS 0 + +namespace android { +// ---------------------------------------------------------------------------- + +SharedClient::SharedClient() + : lock(Mutex::SHARED) +{ +} + +SharedClient::~SharedClient() { +} + + +// these functions are used by the clients +status_t SharedClient::validate(size_t i) const { + if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX)) + return BAD_INDEX; + return surfaces[i].status; +} + +uint32_t SharedClient::getIdentity(size_t token) const { + return uint32_t(surfaces[token].identity); +} + +// ---------------------------------------------------------------------------- + + +SharedBufferStack::SharedBufferStack() +{ +} + +void SharedBufferStack::init(int32_t i) +{ + inUse = -1; + status = NO_ERROR; + identity = i; +} + +status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + + // in the current implementation we only send a single rectangle + const Rect bounds(dirty.getBounds()); + FlatRegion& reg(dirtyRegion[buffer]); + reg.count = 1; + reg.rects[0] = uint16_t(bounds.left); + reg.rects[1] = uint16_t(bounds.top); + reg.rects[2] = uint16_t(bounds.right); + reg.rects[3] = uint16_t(bounds.bottom); + return NO_ERROR; +} + +Region SharedBufferStack::getDirtyRegion(int buffer) const +{ + Region res; + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return res; + + const FlatRegion& reg(dirtyRegion[buffer]); + res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3])); + return res; +} + +// ---------------------------------------------------------------------------- + +SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, + int surface, int num, int32_t identity) + : mSharedClient(sharedClient), + mSharedStack(sharedClient->surfaces + surface), + mNumBuffers(num), mIdentity(identity) +{ +} + +SharedBufferBase::~SharedBufferBase() +{ +} + +uint32_t SharedBufferBase::getIdentity() +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.identity; +} + +status_t SharedBufferBase::getStatus() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.status; +} + +size_t SharedBufferBase::getFrontBuffer() const +{ + SharedBufferStack& stack( *mSharedStack ); + return size_t( stack.head ); +} + +String8 SharedBufferBase::dump(char const* prefix) const +{ + const size_t SIZE = 1024; + char buffer[SIZE]; + String8 result; + SharedBufferStack& stack( *mSharedStack ); + snprintf(buffer, SIZE, + "%s[ head=%2d, available=%2d, queued=%2d ] " + "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n", + prefix, stack.head, stack.available, stack.queued, + stack.reallocMask, stack.inUse, stack.identity, stack.status); + result.append(buffer); + return result; +} + +// ============================================================================ +// conditions and updates +// ============================================================================ + +SharedBufferClient::DequeueCondition::DequeueCondition( + SharedBufferClient* sbc) : ConditionBase(sbc) { +} +bool SharedBufferClient::DequeueCondition::operator()() { + return stack.available > 0; +} + +SharedBufferClient::LockCondition::LockCondition( + SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { +} +bool SharedBufferClient::LockCondition::operator()() { + return (buf != stack.head || + (stack.queued > 0 && stack.inUse != buf)); +} + +SharedBufferServer::ReallocateCondition::ReallocateCondition( + SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) { +} +bool SharedBufferServer::ReallocateCondition::operator()() { + // TODO: we should also check that buf has been dequeued + return (buf != stack.head); +} + +// ---------------------------------------------------------------------------- + +SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) + : UpdateBase(sbb) { +} +ssize_t SharedBufferClient::QueueUpdate::operator()() { + android_atomic_inc(&stack.queued); + return NO_ERROR; +} + +SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb) + : UpdateBase(sbb) { +} +ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() { + android_atomic_inc(&stack.available); + return NO_ERROR; +} + +SharedBufferServer::UnlockUpdate::UnlockUpdate( + SharedBufferBase* sbb, int lockedBuffer) + : UpdateBase(sbb), lockedBuffer(lockedBuffer) { +} +ssize_t SharedBufferServer::UnlockUpdate::operator()() { + if (stack.inUse != lockedBuffer) { + LOGE("unlocking %d, but currently locked buffer is %d", + lockedBuffer, stack.inUse); + return BAD_VALUE; + } + android_atomic_write(-1, &stack.inUse); + return NO_ERROR; +} + +SharedBufferServer::RetireUpdate::RetireUpdate( + SharedBufferBase* sbb, int numBuffers) + : UpdateBase(sbb), numBuffers(numBuffers) { +} +ssize_t SharedBufferServer::RetireUpdate::operator()() { + // head is only written in this function, which is single-thread. + int32_t head = stack.head; + + // Preventively lock the current buffer before updating queued. + android_atomic_write(head, &stack.inUse); + + // Decrement the number of queued buffers + int32_t queued; + do { + queued = stack.queued; + if (queued == 0) { + return NOT_ENOUGH_DATA; + } + } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); + + // update the head pointer + head = ((head+1 >= numBuffers) ? 0 : head+1); + + // lock the buffer before advancing head, which automatically unlocks + // the buffer we preventively locked upon entering this function + android_atomic_write(head, &stack.inUse); + + // advance head + android_atomic_write(head, &stack.head); + + // now that head has moved, we can increment the number of available buffers + android_atomic_inc(&stack.available); + return head; +} + +SharedBufferServer::StatusUpdate::StatusUpdate( + SharedBufferBase* sbb, status_t status) + : UpdateBase(sbb), status(status) { +} + +ssize_t SharedBufferServer::StatusUpdate::operator()() { + android_atomic_write(status, &stack.status); + return NO_ERROR; +} + +// ============================================================================ + +SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, + int surface, int num, int32_t identity) + : SharedBufferBase(sharedClient, surface, num, identity), tail(0) +{ + tail = computeTail(); +} + +int32_t SharedBufferClient::computeTail() const +{ + SharedBufferStack& stack( *mSharedStack ); + // we need to make sure we read available and head coherently, + // w.r.t RetireUpdate. + int32_t newTail; + int32_t avail; + int32_t head; + do { + avail = stack.available; + head = stack.head; + } while (stack.available != avail); + newTail = head - avail + 1; + if (newTail < 0) { + newTail += mNumBuffers; + } + return newTail; +} + +ssize_t SharedBufferClient::dequeue() +{ + SharedBufferStack& stack( *mSharedStack ); + + if (stack.head == tail && stack.available == 2) { + LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", + tail, stack.head, stack.available, stack.queued); + } + + const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); + + //LOGD("[%d] about to dequeue a buffer", + // mSharedStack->identity); + DequeueCondition condition(this); + status_t err = waitForCondition(condition); + if (err != NO_ERROR) + return ssize_t(err); + + // NOTE: 'stack.available' is part of the conditions, however + // decrementing it, never changes any conditions, so we don't need + // to do this as part of an update. + if (android_atomic_dec(&stack.available) == 0) { + LOGW("dequeue probably called from multiple threads!"); + } + + int dequeued = tail; + tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); + LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s", + dequeued, tail, dump("").string()); + + mDequeueTime[dequeued] = dequeueTime; + + return dequeued; +} + +status_t SharedBufferClient::undoDequeue(int buf) +{ + UndoDequeueUpdate update(this); + status_t err = updateCondition( update ); + if (err == NO_ERROR) { + tail = computeTail(); + } + return err; +} + +status_t SharedBufferClient::lock(int buf) +{ + LockCondition condition(this, buf); + status_t err = waitForCondition(condition); + return err; +} + +status_t SharedBufferClient::queue(int buf) +{ + QueueUpdate update(this); + status_t err = updateCondition( update ); + LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); + SharedBufferStack& stack( *mSharedStack ); + const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); + stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); + return err; +} + +bool SharedBufferClient::needNewBuffer(int buffer) const +{ + SharedBufferStack& stack( *mSharedStack ); + const uint32_t mask = 1<<buffer; + return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; +} + +status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setDirtyRegion(buffer, reg); +} + +// ---------------------------------------------------------------------------- + +SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, + int surface, int num, int32_t identity) + : SharedBufferBase(sharedClient, surface, num, identity) +{ + mSharedStack->init(identity); + mSharedStack->head = num-1; + mSharedStack->available = num; + mSharedStack->queued = 0; + mSharedStack->reallocMask = 0; + memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion)); +} + +ssize_t SharedBufferServer::retireAndLock() +{ + RetireUpdate update(this, mNumBuffers); + ssize_t buf = updateCondition( update ); + LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string()); + return buf; +} + +status_t SharedBufferServer::unlock(int buffer) +{ + UnlockUpdate update(this, buffer); + status_t err = updateCondition( update ); + return err; +} + +void SharedBufferServer::setStatus(status_t status) +{ + if (status < NO_ERROR) { + StatusUpdate update(this, status); + updateCondition( update ); + } +} + +status_t SharedBufferServer::reallocate() +{ + SharedBufferStack& stack( *mSharedStack ); + uint32_t mask = (1<<mNumBuffers)-1; + android_atomic_or(mask, &stack.reallocMask); + return NO_ERROR; +} + +int32_t SharedBufferServer::getQueuedCount() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.queued; +} + +status_t SharedBufferServer::assertReallocate(int buffer) +{ + ReallocateCondition condition(this, buffer); + status_t err = waitForCondition(condition); + return err; +} + +Region SharedBufferServer::getDirtyRegion(int buffer) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getDirtyRegion(buffer); +} + +SharedBufferStack::Statistics SharedBufferServer::getStats() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.stats; +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp index 4ea9ae2..f51ca7a 100644 --- a/libs/ui/Surface.cpp +++ b/libs/ui/Surface.cpp @@ -23,233 +23,734 @@ #include <sys/types.h> #include <sys/stat.h> -#include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/IPCThreadState.h> -#include <utils/IMemory.h> +#include <utils/CallStack.h> +#include <binder/IPCThreadState.h> +#include <binder/IMemory.h> #include <utils/Log.h> +#include <ui/DisplayInfo.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferMapper.h> #include <ui/ISurface.h> #include <ui/Surface.h> #include <ui/SurfaceComposerClient.h> #include <ui/Rect.h> -#include <private/ui/SharedState.h> +#include <pixelflinger/pixelflinger.h> + +#include <private/ui/SharedBufferStack.h> #include <private/ui/LayerState.h> namespace android { -// --------------------------------------------------------------------------- +// ---------------------------------------------------------------------- + +static status_t copyBlt( + const sp<GraphicBuffer>& dst, + const sp<GraphicBuffer>& src, + const Region& reg) +{ + status_t err; + uint8_t const * src_bits = NULL; + err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); + LOGE_IF(err, "error locking src buffer %s", strerror(-err)); + + uint8_t* dst_bits = NULL; + err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); + LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); + + Region::const_iterator head(reg.begin()); + Region::const_iterator tail(reg.end()); + if (head != tail && src_bits && dst_bits) { + // NOTE: dst and src must be the same format + const size_t bpp = bytesPerPixel(src->format); + const size_t dbpr = dst->stride * bpp; + const size_t sbpr = src->stride * bpp; + + while (head != tail) { + const Rect& r(*head++); + ssize_t h = r.height(); + if (h <= 0) continue; + size_t size = r.width() * bpp; + uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; + uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + + if (src_bits) + src->unlock(); + + if (dst_bits) + dst->unlock(); + + return err; +} + +// ============================================================================ +// SurfaceControl +// ============================================================================ -Surface::Surface(const sp<SurfaceComposerClient>& client, +SurfaceControl::SurfaceControl( + const sp<SurfaceComposerClient>& client, const sp<ISurface>& surface, const ISurfaceFlingerClient::surface_data_t& data, - uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, - bool owner) + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) : mClient(client), mSurface(surface), mToken(data.token), mIdentity(data.identity), - mFormat(format), mFlags(flags), mOwner(owner) + mWidth(data.width), mHeight(data.height), mFormat(data.format), + mFlags(flags) { - mSwapRectangle.makeInvalid(); - mSurfaceHeapBase[0] = 0; - mSurfaceHeapBase[1] = 0; - mHeap[0] = data.heap[0]; - mHeap[1] = data.heap[1]; } - -Surface::Surface(Surface const* rhs) - : mOwner(false) + +SurfaceControl::~SurfaceControl() { - mToken = rhs->mToken; - mIdentity= rhs->mIdentity; - mClient = rhs->mClient; - mSurface = rhs->mSurface; - mHeap[0] = rhs->mHeap[0]; - mHeap[1] = rhs->mHeap[1]; - mFormat = rhs->mFormat; - mFlags = rhs->mFlags; - mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0]; - mSurfaceHeapBase[1] = rhs->mSurfaceHeapBase[1]; - mSwapRectangle.makeInvalid(); + destroy(); } -Surface::~Surface() +void SurfaceControl::destroy() { - if (mOwner && mToken>=0 && mClient!=0) { + if (isValid()) { mClient->destroySurface(mToken); } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. mClient.clear(); mSurface.clear(); - mHeap[0].clear(); - mHeap[1].clear(); IPCThreadState::self()->flushCommands(); } -sp<Surface> Surface::dup() const +void SurfaceControl::clear() { - Surface const * r = this; - if (this && mOwner) { - // the only reason we need to do this is because of Java's garbage - // collector: because we're creating a copy of the Surface - // instead of a reference, we can garantee that when our last - // reference goes away, the real surface will be deleted. - // Without this hack (the code is correct too), we'd have to - // wait for a GC for the surface to go away. - r = new Surface(this); - } - return const_cast<Surface*>(r); + // here, the window manager tells us explicitly that we should destroy + // the surface's resource. Soon after this call, it will also release + // its last reference (which will call the dtor); however, it is possible + // that a client living in the same process still holds references which + // would delay the call to the dtor -- that is why we need this explicit + // "clear()" call. + destroy(); } -status_t Surface::nextBuffer(SurfaceInfo* info) { - return mClient->nextBuffer(this, info); +bool SurfaceControl::isSameSurface( + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } -status_t Surface::lock(SurfaceInfo* info, bool blocking) { - return Surface::lock(info, NULL, blocking); +status_t SurfaceControl::setLayer(int32_t layer) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setLayer(mToken, layer); +} +status_t SurfaceControl::setPosition(int32_t x, int32_t y) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setPosition(mToken, x, y); +} +status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setSize(mToken, w, h); +} +status_t SurfaceControl::hide() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->hide(mToken); +} +status_t SurfaceControl::show(int32_t layer) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->show(mToken, layer); +} +status_t SurfaceControl::freeze() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->freeze(mToken); +} +status_t SurfaceControl::unfreeze() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->unfreeze(mToken); +} +status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setFlags(mToken, flags, mask); +} +status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setTransparentRegionHint(mToken, transparent); +} +status_t SurfaceControl::setAlpha(float alpha) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setAlpha(mToken, alpha); +} +status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); +} +status_t SurfaceControl::setFreezeTint(uint32_t tint) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setFreezeTint(mToken, tint); } -status_t Surface::lock(SurfaceInfo* info, Region* dirty, bool blocking) { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->lockSurface(this, info, dirty, blocking); +status_t SurfaceControl::validate(SharedClient const* cblk) const +{ + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return NO_INIT; + } + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + return NO_INIT; + } + status_t err = cblk->validate(mToken); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + mToken, mIdentity, err, strerror(-err)); + return err; + } + uint32_t identity = cblk->getIdentity(mToken); + if (mIdentity != identity) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + mToken, mIdentity, identity); + return NO_INIT; + } + return NO_ERROR; } -status_t Surface::unlockAndPost() { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->unlockAndPostSurface(this); +status_t SurfaceControl::writeSurfaceToParcel( + const sp<SurfaceControl>& control, Parcel* parcel) +{ + uint32_t flags = 0; + uint32_t format = 0; + SurfaceID token = -1; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + sp<SurfaceComposerClient> client; + sp<ISurface> sur; + if (SurfaceControl::isValid(control)) { + token = control->mToken; + identity = control->mIdentity; + client = control->mClient; + sur = control->mSurface; + width = control->mWidth; + height = control->mHeight; + format = control->mFormat; + flags = control->mFlags; + } + parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(token); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; } -status_t Surface::unlock() { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->unlockSurface(this); +sp<Surface> SurfaceControl::getSurface() const +{ + Mutex::Autolock _l(mLock); + if (mSurfaceData == 0) { + mSurfaceData = new Surface(const_cast<SurfaceControl*>(this)); + } + return mSurfaceData; } -status_t Surface::setLayer(int32_t layer) { - return mClient->setLayer(this, layer); +// ============================================================================ +// Surface +// ============================================================================ + +Surface::Surface(const sp<SurfaceControl>& surface) + : mClient(surface->mClient), mSurface(surface->mSurface), + mToken(surface->mToken), mIdentity(surface->mIdentity), + mFormat(surface->mFormat), mFlags(surface->mFlags), + mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL), + mWidth(surface->mWidth), mHeight(surface->mHeight) +{ + mSharedBufferClient = new SharedBufferClient( + mClient->mControl, mToken, 2, mIdentity); + + init(); } -status_t Surface::setPosition(int32_t x, int32_t y) { - return mClient->setPosition(this, x, y); + +Surface::Surface(const Parcel& parcel) + : mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL) +{ + sp<IBinder> clientBinder = parcel.readStrongBinder(); + mSurface = interface_cast<ISurface>(parcel.readStrongBinder()); + mToken = parcel.readInt32(); + mIdentity = parcel.readInt32(); + mWidth = parcel.readInt32(); + mHeight = parcel.readInt32(); + mFormat = parcel.readInt32(); + mFlags = parcel.readInt32(); + + // FIXME: what does that mean if clientBinder is NULL here? + if (clientBinder != NULL) { + mClient = SurfaceComposerClient::clientForConnection(clientBinder); + + mSharedBufferClient = new SharedBufferClient( + mClient->mControl, mToken, 2, mIdentity); + } + + init(); } -status_t Surface::setSize(uint32_t w, uint32_t h) { - return mClient->setSize(this, w, h); + +void Surface::init() +{ + android_native_window_t::setSwapInterval = setSwapInterval; + android_native_window_t::dequeueBuffer = dequeueBuffer; + android_native_window_t::lockBuffer = lockBuffer; + android_native_window_t::queueBuffer = queueBuffer; + android_native_window_t::query = query; + android_native_window_t::perform = perform; + mSwapRectangle.makeInvalid(); + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; + const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi; + // FIXME: set real values here + const_cast<int&>(android_native_window_t::minSwapInterval) = 1; + const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; + const_cast<uint32_t&>(android_native_window_t::flags) = 0; + // be default we request a hardware surface + mUsage = GRALLOC_USAGE_HW_RENDER; + mNeedFullUpdate = false; } -status_t Surface::hide() { - return mClient->hide(this); + +Surface::~Surface() +{ + // this is a client-side operation, the surface is destroyed, unmap + // its buffers in this process. + for (int i=0 ; i<2 ; i++) { + if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) { + getBufferMapper().unregisterBuffer(mBuffers[i]->handle); + } + } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mClient.clear(); + mSurface.clear(); + delete mSharedBufferClient; + IPCThreadState::self()->flushCommands(); } -status_t Surface::show(int32_t layer) { - return mClient->show(this, layer); + +sp<SurfaceComposerClient> Surface::getClient() const { + return mClient; } -status_t Surface::freeze() { - return mClient->freeze(this); + +sp<ISurface> Surface::getISurface() const { + return mSurface; } -status_t Surface::unfreeze() { - return mClient->unfreeze(this); + +bool Surface::isValid() { + return mToken>=0 && mClient!=0; } -status_t Surface::setFlags(uint32_t flags, uint32_t mask) { - return mClient->setFlags(this, flags, mask); + +status_t Surface::validate(SharedClient const* cblk) const +{ + sp<SurfaceComposerClient> client(getClient()); + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, client.get()); + return NO_INIT; + } + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + return NO_INIT; + } + status_t err = cblk->validate(mToken); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + mToken, mIdentity, err, strerror(-err)); + return err; + } + uint32_t identity = cblk->getIdentity(mToken); + if (mIdentity != identity) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + mToken, mIdentity, identity); + return NO_INIT; + } + return NO_ERROR; } -status_t Surface::setTransparentRegionHint(const Region& transparent) { - return mClient->setTransparentRegionHint(this, transparent); + + +bool Surface::isSameSurface( + const sp<Surface>& lhs, const sp<Surface>& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } -status_t Surface::setAlpha(float alpha) { - return mClient->setAlpha(this, alpha); + +// ---------------------------------------------------------------------------- + +int Surface::setSwapInterval(android_native_window_t* window, int interval) { + return 0; } -status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy); + +int Surface::dequeueBuffer(android_native_window_t* window, + android_native_buffer_t** buffer) { + Surface* self = getSelf(window); + return self->dequeueBuffer(buffer); } -status_t Surface::setFreezeTint(uint32_t tint) { - return mClient->setFreezeTint(this, tint); + +int Surface::lockBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->lockBuffer(buffer); } -Region Surface::dirtyRegion() const { - return mDirtyRegion; +int Surface::queueBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->queueBuffer(buffer); } -void Surface::setDirtyRegion(const Region& region) const { - mDirtyRegion = region; + +int Surface::query(android_native_window_t* window, + int what, int* value) { + Surface* self = getSelf(window); + return self->query(what, value); } -const Rect& Surface::swapRectangle() const { - return mSwapRectangle; + +int Surface::perform(android_native_window_t* window, + int operation, ...) { + va_list args; + va_start(args, operation); + Surface* self = getSelf(window); + int res = self->perform(operation, args); + va_end(args); + return res; } -void Surface::setSwapRectangle(const Rect& r) { - mSwapRectangle = r; + +// ---------------------------------------------------------------------------- + +status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) { + android_native_buffer_t* out; + status_t err = dequeueBuffer(&out); + if (err == NO_ERROR) { + *buffer = GraphicBuffer::getSelf(out); + } + return err; } -sp<Surface> Surface::readFromParcel(Parcel* parcel) +// ---------------------------------------------------------------------------- + + +int Surface::dequeueBuffer(android_native_buffer_t** buffer) { - sp<SurfaceComposerClient> client; - ISurfaceFlingerClient::surface_data_t data; - sp<IBinder> clientBinder= parcel->readStrongBinder(); - sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder()); - data.heap[0] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); - data.heap[1] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); - data.token = parcel->readInt32(); - data.identity = parcel->readInt32(); - PixelFormat format = parcel->readInt32(); - uint32_t flags = parcel->readInt32(); + sp<SurfaceComposerClient> client(getClient()); + status_t err = validate(client->mControl); + if (err != NO_ERROR) + return err; - if (clientBinder != NULL) - client = SurfaceComposerClient::clientForConnection(clientBinder); + ssize_t bufIdx = mSharedBufferClient->dequeue(); + if (bufIdx < 0) { + LOGE("error dequeuing a buffer (%s)", strerror(bufIdx)); + return bufIdx; + } + + // below we make sure we AT LEAST have the usage flags we want + const uint32_t usage(getUsage()); + const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); + if (backBuffer == 0 || + ((uint32_t(backBuffer->usage) & usage) != usage) || + mSharedBufferClient->needNewBuffer(bufIdx)) + { + err = getBufferLocked(bufIdx, usage); + LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)", + bufIdx, usage, strerror(-err)); + if (err == NO_ERROR) { + // reset the width/height with the what we get from the buffer + mWidth = uint32_t(backBuffer->width); + mHeight = uint32_t(backBuffer->height); + } + } + + // if we still don't have a buffer here, we probably ran out of memory + if (!err && backBuffer==0) { + err = NO_MEMORY; + } - return new Surface(client, surface, data, 0, 0, format, flags, false); + if (err == NO_ERROR) { + mDirtyRegion.set(backBuffer->width, backBuffer->height); + *buffer = backBuffer.get(); + } else { + mSharedBufferClient->undoDequeue(bufIdx); + } + + return err; } -status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) +int Surface::lockBuffer(android_native_buffer_t* buffer) { - uint32_t flags=0; - uint32_t format=0; - SurfaceID token = -1; - uint32_t identity = 0; - sp<SurfaceComposerClient> client; - sp<ISurface> sur; - sp<IMemoryHeap> heap[2]; - if (surface->isValid()) { - token = surface->mToken; - identity = surface->mIdentity; - client = surface->mClient; - sur = surface->mSurface; - heap[0] = surface->mHeap[0]; - heap[1] = surface->mHeap[1]; - format = surface->mFormat; - flags = surface->mFlags; + sp<SurfaceComposerClient> client(getClient()); + status_t err = validate(client->mControl); + if (err != NO_ERROR) + return err; + + int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + err = mSharedBufferClient->lock(bufIdx); + LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); + return err; +} + +int Surface::queueBuffer(android_native_buffer_t* buffer) +{ + sp<SurfaceComposerClient> client(getClient()); + status_t err = validate(client->mControl); + if (err != NO_ERROR) + return err; + + if (mSwapRectangle.isValid()) { + mDirtyRegion.set(mSwapRectangle); } - parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); - parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); - parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); - parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); - parcel->writeInt32(token); - parcel->writeInt32(identity); - parcel->writeInt32(format); - parcel->writeInt32(flags); - return NO_ERROR; + + int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex(); + mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); + err = mSharedBufferClient->queue(bufIdx); + LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); + + if (err == NO_ERROR) { + // FIXME: can we avoid this IPC if we know there is one pending? + client->signalServer(); + } + return err; } -bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs) +int Surface::query(int what, int* value) { - if (lhs == 0 || rhs == 0) - return false; - return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); + switch (what) { + case NATIVE_WINDOW_WIDTH: + *value = int(mWidth); + return NO_ERROR; + case NATIVE_WINDOW_HEIGHT: + *value = int(mHeight); + return NO_ERROR; + case NATIVE_WINDOW_FORMAT: + *value = int(mFormat); + return NO_ERROR; + } + return BAD_VALUE; +} + +int Surface::perform(int operation, va_list args) +{ + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_SET_USAGE: + setUsage( va_arg(args, int) ); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +void Surface::setUsage(uint32_t reqUsage) +{ + Mutex::Autolock _l(mSurfaceLock); + mUsage = reqUsage; +} + +uint32_t Surface::getUsage() const +{ + Mutex::Autolock _l(mSurfaceLock); + return mUsage; +} + +// ---------------------------------------------------------------------------- + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) +{ + if (mApiLock.tryLock() != NO_ERROR) { + LOGE("calling Surface::lock() from different threads!"); + CallStack stack; + stack.update(); + stack.dump("Surface::lock called from different threads"); + return WOULD_BLOCK; + } + + // we're intending to do software rendering from this point + setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + + sp<GraphicBuffer> backBuffer; + status_t err = dequeueBuffer(&backBuffer); + LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); + if (err == NO_ERROR) { + err = lockBuffer(backBuffer.get()); + LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", + backBuffer->getIndex(), strerror(-err)); + if (err == NO_ERROR) { + // we handle copy-back here... + + const Rect bounds(backBuffer->width, backBuffer->height); + Region scratch(bounds); + Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + + if (mNeedFullUpdate) { + // reset newDirtyRegion to bounds when a buffer is reallocated + // it would be better if this information was associated with + // the buffer and made available to outside of Surface. + // This will do for now though. + mNeedFullUpdate = false; + newDirtyRegion.set(bounds); + } else { + newDirtyRegion.andSelf(bounds); + } + + const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); + if (frontBuffer !=0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + !(mFlags & ISurfaceComposer::eDestroyBackbuffer)) + { + const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty() && frontBuffer!=0) { + // copy front to back + copyBlt(backBuffer, frontBuffer, copyback); + } + } + + mDirtyRegion = newDirtyRegion; + mOldDirtyRegion = newDirtyRegion; + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + LOGW_IF(res, "failed locking buffer (handle = %p)", + backBuffer->handle); + + mLockedBuffer = backBuffer; + other->w = backBuffer->width; + other->h = backBuffer->height; + other->s = backBuffer->stride; + other->usage = backBuffer->usage; + other->format = backBuffer->format; + other->bits = vaddr; + } + } + mApiLock.unlock(); + return err; +} + +status_t Surface::unlockAndPost() +{ + if (mLockedBuffer == 0) { + LOGE("unlockAndPost failed, no locked buffer"); + return BAD_VALUE; + } + + status_t err = mLockedBuffer->unlock(); + LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); + + err = queueBuffer(mLockedBuffer.get()); + LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", + mLockedBuffer->getIndex(), strerror(-err)); + + mPostedBuffer = mLockedBuffer; + mLockedBuffer = 0; + return err; } -void* Surface::heapBase(int i) const +void Surface::setSwapRectangle(const Rect& r) { + Mutex::Autolock _l(mSurfaceLock); + mSwapRectangle = r; +} + +status_t Surface::getBufferLocked(int index, int usage) { - void* heapBase = mSurfaceHeapBase[i]; - // map lazily so it doesn't get mapped in clients that don't need it - if (heapBase == 0) { - const sp<IMemoryHeap>& heap(mHeap[i]); - if (heap != 0) { - heapBase = static_cast<uint8_t*>(heap->base()); - if (heapBase == MAP_FAILED) { - heapBase = NULL; - LOGE("Couldn't map Surface's heap (binder=%p, heap=%p)", - heap->asBinder().get(), heap.get()); + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + status_t err = NO_MEMORY; + + // free the current buffer + sp<GraphicBuffer>& currentBuffer(mBuffers[index]); + if (currentBuffer != 0) { + getBufferMapper().unregisterBuffer(currentBuffer->handle); + currentBuffer.clear(); + } + + sp<GraphicBuffer> buffer = s->requestBuffer(index, usage); + LOGE_IF(buffer==0, + "ISurface::getBuffer(%d, %08x) returned NULL", + index, usage); + if (buffer != 0) { // this should never happen by construction + LOGE_IF(buffer->handle == NULL, + "Surface (identity=%d) requestBuffer(%d, %08x) returned" + "a buffer with a null handle", mIdentity, index, usage); + err = mSharedBufferClient->getStatus(); + LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); + if (!err && buffer->handle != NULL) { + err = getBufferMapper().registerBuffer(buffer->handle); + LOGW_IF(err, "registerBuffer(...) failed %d (%s)", + err, strerror(-err)); + if (err == NO_ERROR) { + currentBuffer = buffer; + currentBuffer->setIndex(index); + mNeedFullUpdate = true; } - mSurfaceHeapBase[i] = heapBase; + } else { + err = err<0 ? err : NO_MEMORY; } } - return heapBase; + return err; } }; // namespace android diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp index fe803ff..eda84ef 100644 --- a/libs/ui/SurfaceComposerClient.cpp +++ b/libs/ui/SurfaceComposerClient.cpp @@ -29,26 +29,19 @@ #include <utils/Errors.h> #include <utils/threads.h> #include <utils/KeyedVector.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> -#include <utils/IMemory.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> #include <utils/Log.h> +#include <ui/DisplayInfo.h> #include <ui/ISurfaceComposer.h> #include <ui/ISurfaceFlingerClient.h> #include <ui/ISurface.h> #include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> #include <ui/Rect.h> -#include <ui/Point.h> -#include <private/ui/SharedState.h> #include <private/ui/LayerState.h> -#include <private/ui/SurfaceFlingerSynchro.h> - -#include <pixelflinger/pixelflinger.h> - -#include <utils/BpBinder.h> +#include <private/ui/SharedBufferStack.h> #define VERBOSE(...) ((void)0) //#define VERBOSE LOGD @@ -65,42 +58,50 @@ static Mutex gLock; static sp<ISurfaceComposer> gSurfaceManager; static DefaultKeyedVector< sp<IBinder>, sp<SurfaceComposerClient> > gActiveConnections; static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions; -static sp<IMemory> gServerCblkMemory; +static sp<IMemoryHeap> gServerCblkMemory; static volatile surface_flinger_cblk_t* gServerCblk; -const sp<ISurfaceComposer>& _get_surface_manager() +static sp<ISurfaceComposer> getComposerService() { + sp<ISurfaceComposer> sc; + Mutex::Autolock _l(gLock); if (gSurfaceManager != 0) { - return gSurfaceManager; - } + sc = gSurfaceManager; + } else { + // release the lock while we're waiting... + gLock.unlock(); + + sp<IBinder> binder; + sp<IServiceManager> sm = defaultServiceManager(); + do { + binder = sm->getService(String16("SurfaceFlinger")); + if (binder == 0) { + LOGW("SurfaceFlinger not published, waiting..."); + usleep(500000); // 0.5 s + } + } while(binder == 0); - sp<IBinder> binder; - sp<IServiceManager> sm = defaultServiceManager(); - do { - binder = sm->getService(String16("SurfaceFlinger")); - if (binder == 0) { - LOGW("SurfaceFlinger not published, waiting..."); - usleep(500000); // 0.5 s + // grab the lock again for updating gSurfaceManager + gLock.lock(); + if (gSurfaceManager == 0) { + sc = interface_cast<ISurfaceComposer>(binder); + gSurfaceManager = sc; + } else { + sc = gSurfaceManager; } - } while(binder == 0); - sp<ISurfaceComposer> sc(interface_cast<ISurfaceComposer>(binder)); - - Mutex::Autolock _l(gLock); - if (gSurfaceManager == 0) { - gSurfaceManager = sc; } - return gSurfaceManager; + return sc; } static volatile surface_flinger_cblk_t const * get_cblk() { if (gServerCblk == 0) { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); Mutex::Autolock _l(gLock); if (gServerCblk == 0) { gServerCblkMemory = sm->getCblk(); LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); - gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->pointer(); + gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase(); LOGE_IF(gServerCblk==0, "Can't get server control block address"); } } @@ -109,210 +110,6 @@ static volatile surface_flinger_cblk_t const * get_cblk() // --------------------------------------------------------------------------- -static void copyBlt(const GGLSurface& dst, - const GGLSurface& src, const Region& reg) -{ - Region::iterator iterator(reg); - if (iterator) { - // NOTE: dst and src must be the same format - Rect r; - const size_t bpp = bytesPerPixel(src.format); - const size_t dbpr = dst.stride * bpp; - const size_t sbpr = src.stride * bpp; - while (iterator.iterate(&r)) { - ssize_t h = r.bottom - r.top; - if (h) { - size_t size = (r.right - r.left) * bpp; - uint8_t* s = src.data + (r.left + src.stride * r.top) * bpp; - uint8_t* d = dst.data + (r.left + dst.stride * r.top) * bpp; - if (dbpr==sbpr && size==sbpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += dbpr; - s += sbpr; - } while (--h > 0); - } - } - } -} - -// --------------------------------------------------------------------------- - -surface_flinger_cblk_t::surface_flinger_cblk_t() -{ -} - -// --------------------------------------------------------------------------- - -per_client_cblk_t::per_client_cblk_t() -{ -} - -// these functions are used by the clients -inline status_t per_client_cblk_t::validate(size_t i) const { - if (uint32_t(i) >= NUM_LAYERS_MAX) - return BAD_INDEX; - if (layers[i].swapState & eInvalidSurface) - return NO_MEMORY; - return NO_ERROR; -} - -int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags) -{ - int32_t index; - uint32_t state; - int timeout = 0; - status_t result; - layer_cblk_t * const layer = layers + i; - const bool blocking = flags & BLOCKING; - const bool inspect = flags & INSPECT; - - do { - state = layer->swapState; - - if (UNLIKELY((state&(eFlipRequested|eNextFlipPending)) == eNextFlipPending)) { - LOGE("eNextFlipPending set but eFlipRequested not set, " - "layer=%d (lcblk=%p), state=%08x", - int(i), layer, int(state)); - return INVALID_OPERATION; - } - - if (UNLIKELY(state&eLocked)) { - LOGE("eLocked set when entering lock_layer(), " - "layer=%d (lcblk=%p), state=%08x", - int(i), layer, int(state)); - return WOULD_BLOCK; - } - - - if (state & (eFlipRequested | eNextFlipPending | eResizeRequested - | eInvalidSurface)) - { - int32_t resizeIndex; - Mutex::Autolock _l(lock); - // might block for a very short amount of time - // will never cause the server to block (trylock()) - - goto start_loop_here; - - // We block the client if: - // eNextFlipPending: we've used both buffers already, so we need to - // wait for one to become availlable. - // eResizeRequested: the buffer we're going to acquire is being - // resized. Block until it is done. - // eFlipRequested && eBusy: the buffer we're going to acquire is - // currently in use by the server. - // eInvalidSurface: this is a special case, we don't block in this - // case, we just return an error. - - while((state & (eNextFlipPending|eInvalidSurface)) || - (state & ((resizeIndex) ? eResizeBuffer1 : eResizeBuffer0)) || - ((state & (eFlipRequested|eBusy)) == (eFlipRequested|eBusy)) ) - { - if (state & eInvalidSurface) - return NO_MEMORY; - - if (!blocking) - return WOULD_BLOCK; - - timeout = 0; - result = cv.waitRelative(lock, seconds(1)); - if (__builtin_expect(result!=NO_ERROR, false)) { - const int newState = layer->swapState; - LOGW( "lock_layer timed out (is the CPU pegged?) " - "layer=%d, lcblk=%p, state=%08x (was %08x)", - int(i), layer, newState, int(state)); - timeout = newState != int(state); - } - - start_loop_here: - state = layer->swapState; - resizeIndex = (state&eIndex) ^ ((state&eFlipRequested)>>1); - } - - LOGW_IF(timeout, - "lock_layer() timed out but didn't appear to need " - "to be locked and we recovered " - "(layer=%d, lcblk=%p, state=%08x)", - int(i), layer, int(state)); - } - - // eFlipRequested is not set and cannot be set by another thread: it's - // safe to use the first buffer without synchronization. - - // Choose the index depending on eFlipRequested. - // When it's set, choose the 'other' buffer. - index = (state&eIndex) ^ ((state&eFlipRequested)>>1); - - // make sure this buffer is valid - if (layer->surface[index].bits_offset < 0) { - return status_t(layer->surface[index].bits_offset); - } - - if (inspect) { - // we just want to inspect this layer. don't lock it. - goto done; - } - - // last thing before we're done, we need to atomically lock the state - } while (android_atomic_cmpxchg(state, state|eLocked, &(layer->swapState))); - - VERBOSE("locked layer=%d (lcblk=%p), buffer=%d, state=0x%08x", - int(i), layer, int(index), int(state)); - - // store the index of the locked buffer (for client use only) - layer->flags &= ~eBufferIndex; - layer->flags |= ((index << eBufferIndexShift) & eBufferIndex); - -done: - return index; -} - -uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) -{ - // atomically set eFlipRequested and clear eLocked and optionnaly - // set eNextFlipPending if eFlipRequested was already set - - layer_cblk_t * const layer = layers + i; - int32_t oldvalue, newvalue; - do { - oldvalue = layer->swapState; - // get current value - - newvalue = oldvalue & ~eLocked; - // clear eLocked - - newvalue |= eFlipRequested; - // set eFlipRequested - - if (oldvalue & eFlipRequested) - newvalue |= eNextFlipPending; - // if eFlipRequested was alread set, set eNextFlipPending - - } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState))); - - VERBOSE("request pageflip for layer=%d, buffer=%d, state=0x%08x", - int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift), - int(newvalue)); - - // from this point, the server can kick in at anytime and use the first - // buffer, so we cannot use it anymore, and we must use the 'other' - // buffer instead (or wait if it is not availlable yet, see lock_layer). - - return newvalue; -} - -void per_client_cblk_t::unlock_layer(size_t i) -{ - layer_cblk_t * const layer = layers + i; - android_atomic_and(~eLocked, &layer->swapState); -} - -// --------------------------------------------------------------------------- - static inline int compare_type( const layer_state_t& lhs, const layer_state_t& rhs) { if (lhs.surface < rhs.surface) return -1; @@ -322,7 +119,7 @@ static inline int compare_type( const layer_state_t& lhs, SurfaceComposerClient::SurfaceComposerClient() { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); if (sm == 0) { _init(0, 0); return; @@ -343,12 +140,20 @@ SurfaceComposerClient::SurfaceComposerClient( _init(sm, interface_cast<ISurfaceFlingerClient>(conn)); } + +status_t SurfaceComposerClient::linkToComposerDeath( + const sp<IBinder::DeathRecipient>& recipient, + void* cookie, uint32_t flags) +{ + sp<ISurfaceComposer> sm(getComposerService()); + return sm->asBinder()->linkToDeath(recipient, cookie, flags); +} + void SurfaceComposerClient::_init( const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn) { VERBOSE("Creating client %p, conn %p", this, conn.get()); - mSignalServer = 0; mPrebuiltLayerState = 0; mTransactionOpen = 0; mStatus = NO_ERROR; @@ -360,9 +165,9 @@ void SurfaceComposerClient::_init( return; } - mClient->getControlBlocks(&mControlMemory); - mSignalServer = new SurfaceFlingerSynchro(sm); - mControl = static_cast<per_client_cblk_t *>(mControlMemory->pointer()); + mControlMemory = mClient->getControlBlock(); + mSignalServer = sm; + mControl = static_cast<SharedClient *>(mControlMemory->getBase()); } SurfaceComposerClient::~SurfaceComposerClient() @@ -376,32 +181,6 @@ status_t SurfaceComposerClient::initCheck() const return mStatus; } -status_t SurfaceComposerClient::validateSurface( - per_client_cblk_t const* cblk, Surface const * surface) -{ - SurfaceID index = surface->ID(); - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", - index, surface->getIdentity()); - return NO_INIT; - } - - status_t err = cblk->validate(index); - if (err != NO_ERROR) { - LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", - index, surface->getIdentity(), err, strerror(-err)); - return err; - } - - if (surface->getIdentity() != uint32_t(cblk->layers[index].identity)) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - index, surface->getIdentity(), cblk->layers[index].identity); - return NO_INIT; - } - - return NO_ERROR; -} - sp<IBinder> SurfaceComposerClient::connection() const { return (mClient != 0) ? mClient->asBinder() : 0; @@ -419,7 +198,7 @@ SurfaceComposerClient::clientForConnection(const sp<IBinder>& conn) if (client == 0) { // Need to make a new client. - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); client = new SurfaceComposerClient(sm, conn); if (client != 0 && client->initCheck() == NO_ERROR) { Mutex::Autolock _l(gLock); @@ -437,15 +216,13 @@ void SurfaceComposerClient::dispose() { // this can be called more than once. - sp<IMemory> controlMemory; + sp<IMemoryHeap> controlMemory; sp<ISurfaceFlingerClient> client; - sp<IMemoryHeap> surfaceHeap; { Mutex::Autolock _lg(gLock); Mutex::Autolock _lm(mLock); - delete mSignalServer; mSignalServer = 0; if (mClient != 0) { @@ -462,9 +239,7 @@ void SurfaceComposerClient::dispose() delete mPrebuiltLayerState; mPrebuiltLayerState = 0; controlMemory = mControlMemory; - surfaceHeap = mSurfaceHeap; mControlMemory.clear(); - mSurfaceHeap.clear(); mControl = 0; mStatus = NO_INIT; } @@ -528,7 +303,13 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays() return n; } -sp<Surface> SurfaceComposerClient::createSurface( + +void SurfaceComposerClient::signalServer() +{ + mSignalServer->signal(); +} + +sp<SurfaceControl> SurfaceComposerClient::createSurface( int pid, DisplayID display, uint32_t w, @@ -536,14 +317,14 @@ sp<Surface> SurfaceComposerClient::createSurface( PixelFormat format, uint32_t flags) { - sp<Surface> result; + sp<SurfaceControl> result; if (mStatus == NO_ERROR) { ISurfaceFlingerClient::surface_data_t data; sp<ISurface> surface = mClient->createSurface(&data, pid, display, w, h, format, flags); if (surface != 0) { if (uint32_t(data.token) < NUM_LAYERS_MAX) { - result = new Surface(this, surface, data, w, h, format, flags); + result = new SurfaceControl(this, surface, data, w, h, format, flags); } } } @@ -568,186 +349,6 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid) return err; } -status_t SurfaceComposerClient::nextBuffer(Surface* surface, - Surface::SurfaceInfo* info) -{ - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - int32_t backIdx = surface->mBackbufferIndex; - layer_cblk_t* const lcblk = &(cblk->layers[index]); - const surface_info_t* const front = lcblk->surface + (1-backIdx); - info->w = front->w; - info->h = front->h; - info->format = front->format; - info->base = surface->heapBase(1-backIdx); - info->bits = reinterpret_cast<void*>(intptr_t(info->base) + front->bits_offset); - info->bpr = front->bpr; - - return 0; -} - -status_t SurfaceComposerClient::lockSurface( - Surface* surface, - Surface::SurfaceInfo* other, - Region* dirty, - bool blocking) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - int32_t backIdx = cblk->lock_layer(size_t(index), - per_client_cblk_t::BLOCKING); - if (backIdx >= 0) { - surface->mBackbufferIndex = backIdx; - layer_cblk_t* const lcblk = &(cblk->layers[index]); - const surface_info_t* const back = lcblk->surface + backIdx; - const surface_info_t* const front = lcblk->surface + (1-backIdx); - other->w = back->w; - other->h = back->h; - other->format = back->format; - other->base = surface->heapBase(backIdx); - other->bits = reinterpret_cast<void*>(intptr_t(other->base) + back->bits_offset); - other->bpr = back->bpr; - - const Rect bounds(other->w, other->h); - Region newDirtyRegion; - - if (back->flags & surface_info_t::eBufferDirty) { - /* it is safe to write *back here, because we're guaranteed - * SurfaceFlinger is not touching it (since it just granted - * access to us) */ - const_cast<surface_info_t*>(back)->flags &= - ~surface_info_t::eBufferDirty; - - // content is meaningless in this case and the whole surface - // needs to be redrawn. - - newDirtyRegion.set(bounds); - if (dirty) { - *dirty = newDirtyRegion; - } - - //if (bytesPerPixel(other->format) == 4) { - // android_memset32( - // (uint32_t*)other->bits, 0xFF00FF00, other->h * other->bpr); - //} else { - // android_memset16( // fill with green - // (uint16_t*)other->bits, 0x7E0, other->h * other->bpr); - //} - } - else - { - if (dirty) { - dirty->andSelf(Region(bounds)); - newDirtyRegion = *dirty; - } else { - newDirtyRegion.set(bounds); - } - - Region copyback; - if (!(lcblk->flags & eNoCopyBack)) { - const Region previousDirtyRegion(surface->dirtyRegion()); - copyback = previousDirtyRegion.subtract(newDirtyRegion); - } - - if (!copyback.isEmpty()) { - // copy front to back - GGLSurface cb; - cb.version = sizeof(GGLSurface); - cb.width = back->w; - cb.height = back->h; - cb.stride = back->stride; - cb.data = (GGLubyte*)surface->heapBase(backIdx); - cb.data += back->bits_offset; - cb.format = back->format; - - GGLSurface t; - t.version = sizeof(GGLSurface); - t.width = front->w; - t.height = front->h; - t.stride = front->stride; - t.data = (GGLubyte*)surface->heapBase(1-backIdx); - t.data += front->bits_offset; - t.format = front->format; - - //const Region copyback(lcblk->region + 1-backIdx); - copyBlt(cb, t, copyback); - } - } - - // update dirty region - surface->setDirtyRegion(newDirtyRegion); - } - return (backIdx < 0) ? status_t(backIdx) : status_t(NO_ERROR); -} - -void SurfaceComposerClient::_signal_server() -{ - mSignalServer->signal(); -} - -void SurfaceComposerClient::_send_dirty_region( - layer_cblk_t* lcblk, const Region& dirty) -{ - const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; - flat_region_t* flat_region = lcblk->region + index; - status_t err = dirty.write(flat_region, sizeof(flat_region_t)); - if (err < NO_ERROR) { - // region doesn't fit, use the bounds - const Region reg(dirty.bounds()); - reg.write(flat_region, sizeof(flat_region_t)); - } -} - -status_t SurfaceComposerClient::unlockAndPostSurface(Surface* surface) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - Region dirty(surface->dirtyRegion()); - const Rect& swapRect(surface->swapRectangle()); - if (swapRect.isValid()) { - dirty.set(swapRect); - } - - // transmit the dirty region - layer_cblk_t* const lcblk = &(cblk->layers[index]); - _send_dirty_region(lcblk, dirty); - uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); - if (!(newstate & eNextFlipPending)) - _signal_server(); - return NO_ERROR; -} - -status_t SurfaceComposerClient::unlockSurface(Surface* surface) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - layer_cblk_t* const lcblk = &(cblk->layers[index]); - cblk->unlock_layer(size_t(index)); - return NO_ERROR; -} - void SurfaceComposerClient::openGlobalTransaction() { Mutex::Autolock _l(gLock); @@ -789,34 +390,33 @@ void SurfaceComposerClient::closeGlobalTransaction() const size_t N = clients.size(); VERBOSE("closeGlobalTransaction (%ld clients)", N); - if (N == 1) { - clients[0]->closeTransaction(); - } else { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); - sm->openGlobalTransaction(); - for (size_t i=0; i<N; i++) { - clients[i]->closeTransaction(); - } - sm->closeGlobalTransaction(); + + sp<ISurfaceComposer> sm(getComposerService()); + sm->openGlobalTransaction(); + for (size_t i=0; i<N; i++) { + clients[i]->closeTransaction(); } + sm->closeGlobalTransaction(); + } + status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags) { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); return sm->freezeDisplay(dpy, flags); } status_t SurfaceComposerClient::unfreezeDisplay(DisplayID dpy, uint32_t flags) { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); return sm->unfreezeDisplay(dpy, flags); } int SurfaceComposerClient::setOrientation(DisplayID dpy, int orientation, uint32_t flags) { - const sp<ISurfaceComposer>& sm(_get_surface_manager()); + sp<ISurfaceComposer> sm(getComposerService()); return sm->setOrientation(dpy, orientation, flags); } @@ -866,14 +466,8 @@ status_t SurfaceComposerClient::closeTransaction() return NO_ERROR; } -layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface) +layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) { - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface.get()); - if (err != NO_ERROR) - return 0; - // API usage error, do nothing. if (mTransactionOpen<=0) { LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", @@ -892,11 +486,11 @@ layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface) return mStates.editArray() + i; } -layer_state_t* SurfaceComposerClient::_lockLayerState(const sp<Surface>& surface) +layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id) { layer_state_t* s; mLock.lock(); - s = _get_state_l(surface); + s = _get_state_l(id); if (!s) mLock.unlock(); return s; } @@ -906,9 +500,9 @@ void SurfaceComposerClient::_unlockLayerState() mLock.unlock(); } -status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t y) +status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::ePositionChanged; s->x = x; @@ -917,9 +511,9 @@ status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t return NO_ERROR; } -status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h) +status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eSizeChanged; s->w = w; @@ -928,9 +522,9 @@ status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h return NO_ERROR; } -status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) +status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eLayerChanged; s->z = z; @@ -938,32 +532,32 @@ status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) return NO_ERROR; } -status_t SurfaceComposerClient::hide(Surface* surface) +status_t SurfaceComposerClient::hide(SurfaceID id) { - return setFlags(surface, ISurfaceComposer::eLayerHidden, + return setFlags(id, ISurfaceComposer::eLayerHidden, ISurfaceComposer::eLayerHidden); } -status_t SurfaceComposerClient::show(Surface* surface, int32_t) +status_t SurfaceComposerClient::show(SurfaceID id, int32_t) { - return setFlags(surface, 0, ISurfaceComposer::eLayerHidden); + return setFlags(id, 0, ISurfaceComposer::eLayerHidden); } -status_t SurfaceComposerClient::freeze(Surface* surface) +status_t SurfaceComposerClient::freeze(SurfaceID id) { - return setFlags(surface, ISurfaceComposer::eLayerFrozen, + return setFlags(id, ISurfaceComposer::eLayerFrozen, ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::unfreeze(Surface* surface) +status_t SurfaceComposerClient::unfreeze(SurfaceID id) { - return setFlags(surface, 0, ISurfaceComposer::eLayerFrozen); + return setFlags(id, 0, ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::setFlags(Surface* surface, +status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, uint32_t mask) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eVisibilityChanged; s->flags &= ~mask; @@ -973,11 +567,10 @@ status_t SurfaceComposerClient::setFlags(Surface* surface, return NO_ERROR; } - status_t SurfaceComposerClient::setTransparentRegionHint( - Surface* surface, const Region& transparentRegion) + SurfaceID id, const Region& transparentRegion) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eTransparentRegionChanged; s->transparentRegion = transparentRegion; @@ -985,9 +578,9 @@ status_t SurfaceComposerClient::setTransparentRegionHint( return NO_ERROR; } -status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) +status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eAlphaChanged; s->alpha = alpha; @@ -996,11 +589,11 @@ status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) } status_t SurfaceComposerClient::setMatrix( - Surface* surface, + SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy ) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eMatrixChanged; layer_state_t::matrix22_t matrix; @@ -1013,9 +606,9 @@ status_t SurfaceComposerClient::setMatrix( return NO_ERROR; } -status_t SurfaceComposerClient::setFreezeTint(Surface* surface, uint32_t tint) +status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eFreezeTintChanged; s->tint = tint; diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp deleted file mode 100644 index 5cd9755..0000000 --- a/libs/ui/SurfaceFlingerSynchro.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "SurfaceFlingerSynchro" - -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <utils/IPCThreadState.h> -#include <utils/Log.h> - -#include <private/ui/SurfaceFlingerSynchro.h> - -namespace android { - -// --------------------------------------------------------------------------- - -SurfaceFlingerSynchro::Barrier::Barrier() - : state(CLOSED) { -} - -SurfaceFlingerSynchro::Barrier::~Barrier() { -} - -void SurfaceFlingerSynchro::Barrier::open() { - asm volatile ("":::"memory"); - Mutex::Autolock _l(lock); - state = OPENED; - cv.broadcast(); -} - -void SurfaceFlingerSynchro::Barrier::close() { - Mutex::Autolock _l(lock); - state = CLOSED; -} - -void SurfaceFlingerSynchro::Barrier::waitAndClose() -{ - Mutex::Autolock _l(lock); - while (state == CLOSED) { - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - cv.wait(lock); - } - state = CLOSED; -} - -status_t SurfaceFlingerSynchro::Barrier::waitAndClose(nsecs_t timeout) -{ - Mutex::Autolock _l(lock); - while (state == CLOSED) { - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - int err = cv.waitRelative(lock, timeout); - if (err != 0) - return err; - } - state = CLOSED; - return NO_ERROR; -} - -// --------------------------------------------------------------------------- - -SurfaceFlingerSynchro::SurfaceFlingerSynchro(const sp<ISurfaceComposer>& flinger) - : mSurfaceComposer(flinger) -{ -} - -SurfaceFlingerSynchro::SurfaceFlingerSynchro() -{ -} - -SurfaceFlingerSynchro::~SurfaceFlingerSynchro() -{ -} - -status_t SurfaceFlingerSynchro::signal() -{ - mSurfaceComposer->signal(); - return NO_ERROR; -} - -status_t SurfaceFlingerSynchro::wait() -{ - mBarrier.waitAndClose(); - return NO_ERROR; -} - -status_t SurfaceFlingerSynchro::wait(nsecs_t timeout) -{ - if (timeout == 0) - return SurfaceFlingerSynchro::wait(); - return mBarrier.waitAndClose(timeout); -} - -void SurfaceFlingerSynchro::open() -{ - mBarrier.open(); -} - -// --------------------------------------------------------------------------- - -}; // namespace android - diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk new file mode 100644 index 0000000..6cc4a5a --- /dev/null +++ b/libs/ui/tests/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + region.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-region + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region.cpp new file mode 100644 index 0000000..ef15de9 --- /dev/null +++ b/libs/ui/tests/region.cpp @@ -0,0 +1,69 @@ +/* + * 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 "Region" + +#include <stdio.h> +#include <utils/Debug.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +using namespace android; + +int main() +{ + Region empty; + Region reg0( Rect( 0, 0, 100, 100 ) ); + Region reg1 = reg0; + Region reg2, reg3; + + Region reg4 = empty | reg1; + Region reg5 = reg1 | empty; + + reg4.dump("reg4"); + reg5.dump("reg5"); + + reg0.dump("reg0"); + reg1.dump("reg1"); + + reg0 = reg0 | reg0.translate(150, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + + reg0 = reg0 | reg0.translate(300, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + + //reg2 = reg0 | reg0.translate(0, 100); + //reg0.dump("reg0"); + //reg1.dump("reg1"); + //reg2.dump("reg2"); + + //reg3 = reg0 | reg0.translate(0, 150); + //reg0.dump("reg0"); + //reg1.dump("reg1"); + //reg2.dump("reg2"); + //reg3.dump("reg3"); + + LOGD("---"); + reg2 = reg0 | reg0.translate(100, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + reg2.dump("reg2"); + + return 0; +} + diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 9bdd64a..59409a2 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -32,52 +32,24 @@ commonSources:= \ StopWatch.cpp \ String8.cpp \ String16.cpp \ + StringArray.cpp \ SystemClock.cpp \ TextOutput.cpp \ Threads.cpp \ - TimerProbe.cpp \ Timers.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ ZipUtils.cpp \ - misc.cpp \ - ported.cpp \ - LogSocket.cpp + misc.cpp -# -# The cpp files listed here do not belong in the device -# build. Consult with the swetland before even thinking about -# putting them in commonSources. -# -# They're used by the simulator runtime and by host-side tools like -# aapt and the simulator front-end. -# -hostSources:= \ - InetAddress.cpp \ - Pipe.cpp \ - Socket.cpp \ - ZipEntry.cpp \ - ZipFile.cpp # For the host # ===================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) $(hostSources) - -ifeq ($(HOST_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe - LOCAL_SRC_FILES += \ - futex_synchro.c \ - executablepath_linux.cpp -endif -ifeq ($(HOST_OS),darwin) - LOCAL_SRC_FILES += \ - executablepath_darwin.cpp -endif +LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= libutils @@ -103,37 +75,18 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - Binder.cpp \ - BpBinder.cpp \ - IInterface.cpp \ - IMemory.cpp \ - IPCThreadState.cpp \ - MemoryDealer.cpp \ - MemoryBase.cpp \ - MemoryHeapBase.cpp \ - MemoryHeapPmem.cpp \ - Parcel.cpp \ - ProcessState.cpp \ - IPermissionController.cpp \ - IServiceManager.cpp \ Unicode.cpp \ BackupData.cpp \ BackupHelpers.cpp -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_SRC_FILES += $(hostSources) -endif - ifeq ($(TARGET_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe -LOCAL_SRC_FILES += futex_synchro.c LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ external/zlib \ external/icu4c/common + LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ @@ -144,15 +97,10 @@ LOCAL_SHARED_LIBRARIES := \ ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) # This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp -LOCAL_SHARED_LIBRARIES += \ - libdl +LOCAL_SHARED_LIBRARIES += libdl endif # linux-x86 endif # sim LOCAL_MODULE:= libutils - -#LOCAL_CFLAGS+= -#LOCAL_LDFLAGS:= - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index cce754a..adb3174 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -107,7 +107,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - if (true) { + if (false) { LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } @@ -193,8 +193,11 @@ BackupDataReader::Status() if ((actual) != (expected)) { \ if ((actual) == 0) { \ m_status = EIO; \ + m_done = true; \ } else { \ m_status = errno; \ + LOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ + long(actual), long(expected), __LINE__, strerror(m_status)); \ } \ return m_status; \ } \ @@ -203,6 +206,7 @@ BackupDataReader::Status() do { \ status_t err = skip_padding(); \ if (err != NO_ERROR) { \ + LOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ m_status = err; \ return err; \ } \ @@ -218,10 +222,19 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) int amt; - // No error checking here, in case we're at the end of the stream. Just let read() fail. - skip_padding(); + amt = skip_padding(); + if (amt == EIO) { + *done = m_done = true; + return NO_ERROR; + } + else if (amt != NO_ERROR) { + return amt; + } amt = read(m_fd, &m_header, sizeof(m_header)); *done = m_done = (amt == 0); + if (*done) { + return NO_ERROR; + } CHECK_SIZE(amt, sizeof(m_header)); m_pos += sizeof(m_header); if (type) { @@ -298,10 +311,12 @@ BackupDataReader::SkipEntityData() } if (m_header.entity.dataSize > 0) { int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); - return pos == -1 ? (int)errno : (int)NO_ERROR; - } else { - return NO_ERROR; + if (pos == -1) { + return errno; + } } + SKIP_PADDING(); + return NO_ERROR; } ssize_t @@ -325,6 +340,10 @@ BackupDataReader::ReadEntityData(void* data, size_t size) m_status = errno; return -1; } + if (amt == 0) { + m_status = EIO; + m_done = true; + } m_pos += amt; return amt; } diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 2fdaa71..55b6024 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -311,7 +311,8 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const } else { void const* start = 0; name = MapInfo::mapAddressToName(ip, "<unknown>", &start); - snprintf(tmp, 256, "pc %08lx %s", uintptr_t(ip)-uintptr_t(start), name); + snprintf(tmp, 256, "pc %08lx %s", + long(uintptr_t(ip)-uintptr_t(start)), name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp deleted file mode 100644 index c6d49aa..0000000 --- a/libs/utils/IDataConnection.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 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 <stdint.h> -#include <sys/types.h> - -#include <utils/Parcel.h> - -#include <utils/IDataConnection.h> - -namespace android { - -// --------------------------------------------------------------------------- - -enum -{ - CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 -}; - -class BpDataConnection : public BpInterface<IDataConnection> -{ -public: - BpDataConnection::BpDataConnection(const sp<IBinder>& impl) - : BpInterface<IDataConnection>(impl) - { - } - - virtual void connect() - { - Parcel data, reply; - data.writeInterfaceToken(IDataConnection::descriptor()); - remote()->transact(CONNECT_TRANSACTION, data, &reply); - } - - virtual void disconnect() - { - Parcel data, reply; - remote()->transact(DISCONNECT_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) - { - case CONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - connect(); - return NO_ERROR; - } - - case DISCONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - disconnect(); - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp deleted file mode 100644 index 39a0a68..0000000 --- a/libs/utils/InetAddress.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// -#ifdef HAVE_WINSOCK -# include <winsock2.h> -#else -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -//# include <arpa/inet.h> -# include <netdb.h> -#endif - -#include <utils/inet_address.h> -#include <utils/threads.h> -#include <utils/Log.h> - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -using namespace android; - - -/* - * =========================================================================== - * InetAddress - * =========================================================================== - */ - -// lock for the next couple of functions; could tuck into InetAddress -static Mutex* gGHBNLock; - -/* - * Lock/unlock access to the hostent struct returned by gethostbyname(). - */ -static inline void lock_gethostbyname(void) -{ - if (gGHBNLock == NULL) - gGHBNLock = new Mutex; - gGHBNLock->lock(); -} -static inline void unlock_gethostbyname(void) -{ - assert(gGHBNLock != NULL); - gGHBNLock->unlock(); -} - - -/* - * Constructor -- just init members. This is private so that callers - * are required to use getByName(). - */ -InetAddress::InetAddress(void) - : mAddress(NULL), mLength(-1), mName(NULL) -{ -} - -/* - * Destructor -- free address storage. - */ -InetAddress::~InetAddress(void) -{ - delete[] (char*) mAddress; - delete[] mName; -} - -/* - * Copy constructor. - */ -InetAddress::InetAddress(const InetAddress& orig) -{ - *this = orig; // use assignment code -} - -/* - * Assignment operator. - */ -InetAddress& InetAddress::operator=(const InetAddress& addr) -{ - // handle self-assignment - if (this == &addr) - return *this; - // copy mLength and mAddress - mLength = addr.mLength; - if (mLength > 0) { - mAddress = new char[mLength]; - memcpy(mAddress, addr.mAddress, mLength); - LOG(LOG_DEBUG, "socket", - "HEY: copied %d bytes in assignment operator\n", mLength); - } else { - mAddress = NULL; - } - // copy mName - mName = new char[strlen(addr.mName)+1]; - strcpy(mName, addr.mName); - - return *this; -} - -/* - * Create a new object from a name or a dotted-number IP notation. - * - * Returns NULL on failure. - */ -InetAddress* -InetAddress::getByName(const char* host) -{ - InetAddress* newAddr = NULL; - struct sockaddr_in addr; - struct hostent* he; - DurationTimer hostTimer, lockTimer; - - // gethostbyname() isn't reentrant, so we need to lock things until - // we can copy the data out. - lockTimer.start(); - lock_gethostbyname(); - hostTimer.start(); - - he = gethostbyname(host); - if (he == NULL) { - LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); - unlock_gethostbyname(); - return NULL; - } - - memcpy(&addr.sin_addr, he->h_addr, he->h_length); - addr.sin_family = he->h_addrtype; - addr.sin_port = 0; - - // got it, unlock us - hostTimer.stop(); - he = NULL; - unlock_gethostbyname(); - - lockTimer.stop(); - if ((long) lockTimer.durationUsecs() > 100000) { - long lockTime = (long) lockTimer.durationUsecs(); - long hostTime = (long) hostTimer.durationUsecs(); - LOG(LOG_DEBUG, "socket", - "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", - host, lockTime / 1000000.0, hostTime / 1000000.0, - (lockTime - hostTime) / 1000000.0); - } - - // Alloc storage and copy it over. - newAddr = new InetAddress(); - if (newAddr == NULL) - return NULL; - - newAddr->mLength = sizeof(struct sockaddr_in); - newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; - if (newAddr->mAddress == NULL) { - delete newAddr; - return NULL; - } - memcpy(newAddr->mAddress, &addr, newAddr->mLength); - - // Keep this for debug messages. - newAddr->mName = new char[strlen(host)+1]; - if (newAddr->mName == NULL) { - delete newAddr; - return NULL; - } - strcpy(newAddr->mName, host); - - return newAddr; -} - - -/* - * =========================================================================== - * InetSocketAddress - * =========================================================================== - */ - -/* - * Create an address with the host wildcard (INADDR_ANY). - */ -bool InetSocketAddress::create(int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName("0.0.0.0"); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const InetAddress* addr, int port) -{ - assert(mAddress == NULL); - - mAddress = new InetAddress(*addr); // make a copy - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const char* host, int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName(host); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp deleted file mode 100644 index 55c1b99..0000000 --- a/libs/utils/LogSocket.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * 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 HAVE_WINSOCK -//#define SOCKETLOG -#endif - -#ifdef SOCKETLOG - -#define LOG_TAG "SOCKETLOG" - -#include <string.h> -#include <cutils/log.h> -#include "utils/LogSocket.h" -#include "utils/logger.h" -#include "cutils/hashmap.h" - -// defined in //device/data/etc/event-log-tags -#define SOCKET_CLOSE_LOG 51000 - -static Hashmap* statsMap = NULL; - -#define LOG_LIST_NUMBER 5 - -typedef struct SocketStats { - int fd; - unsigned int send; - unsigned int recv; - unsigned int ip; - unsigned short port; - short reason; -}SocketStats; - -SocketStats *get_socket_stats(int fd) { - if (statsMap == NULL) { - statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); - } - - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s == NULL) { - // LOGD("create SocketStats for fd %d", fd); - s = (SocketStats*) malloc(sizeof(SocketStats)); - memset(s, 0, sizeof(SocketStats)); - s->fd = fd; - hashmapPut(statsMap, &s->fd, s); - } - return s; -} - -void log_socket_connect(int fd, unsigned int ip, unsigned short port) { - // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); - SocketStats *s = get_socket_stats(fd); - s->ip = ip; - s->port = port; -} - -void add_send_stats(int fd, int send) { - if (send <=0) { - LOGE("add_send_stats send %d", send); - return; - } - SocketStats *s = get_socket_stats(fd); - s->send += send; - // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -void add_recv_stats(int fd, int recv) { - if (recv <=0) { - LOGE("add_recv_stats recv %d", recv); - return; - } - SocketStats *s = get_socket_stats(fd); - s->recv += recv; - // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -char* put_int(char* buf, int value) { - *buf = EVENT_TYPE_INT; - buf++; - memcpy(buf, &value, sizeof(int)); - return buf + sizeof(int); -} - -void log_socket_close(int fd, short reason) { - if (statsMap) { - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s != NULL) { - if (s->send != 0 || s->recv != 0) { - s->reason = reason; - // 5 int + list type need 2 bytes - char buf[LOG_LIST_NUMBER * 5 + 2]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = LOG_LIST_NUMBER; - char* writePos = buf + 2; - writePos = put_int(writePos, s->send); - writePos = put_int(writePos, s->recv); - writePos = put_int(writePos, s->ip); - writePos = put_int(writePos, s->port); - writePos = put_int(writePos, s->reason); - - android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); - // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); - } - hashmapRemove(statsMap, &s->fd); - free(s); - } - } -} - -#else -void add_send_stats(int fd, int send) {} -void add_recv_stats(int fd, int recv) {} -void log_socket_close(int fd, short reason) {} -void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} -#endif diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp deleted file mode 100644 index 613906b..0000000 --- a/libs/utils/Pipe.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Unidirectional pipe. -// - -#include <utils/Pipe.h> -#include <utils/Log.h> - -#if defined(HAVE_WIN32_IPC) -# include <windows.h> -#else -# include <fcntl.h> -# include <unistd.h> -# include <errno.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> -#include <string.h> - -using namespace android; - -const unsigned long kInvalidHandle = (unsigned long) -1; - - -/* - * Constructor. Do little. - */ -Pipe::Pipe(void) - : mReadNonBlocking(false), mReadHandle(kInvalidHandle), - mWriteHandle(kInvalidHandle) -{ -} - -/* - * Destructor. Use the system-appropriate close call. - */ -Pipe::~Pipe(void) -{ -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) - LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", - mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - FlushFileBuffers((HANDLE)mWriteHandle); - if (!CloseHandle((HANDLE)mWriteHandle)) - LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", - mWriteHandle); - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", - (int) mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", - (int) mWriteHandle); - } -#endif -} - -/* - * Create the pipe. - * - * Use the POSIX stuff for everything but Windows. - */ -bool Pipe::create(void) -{ - assert(mReadHandle == kInvalidHandle); - assert(mWriteHandle == kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - /* we use this across processes, so they need to be inheritable */ - HANDLE handles[2]; - SECURITY_ATTRIBUTES saAttr; - - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = (unsigned long) handles[0]; - mWriteHandle = (unsigned long) handles[1]; - return true; -#else - int fds[2]; - - if (pipe(fds) != 0) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = fds[0]; - mWriteHandle = fds[1]; - return true; -#endif -} - -/* - * Create a "half pipe". Please, no Segway riding. - */ -bool Pipe::createReader(unsigned long handle) -{ - mReadHandle = handle; - assert(mWriteHandle == kInvalidHandle); - return true; -} - -/* - * Create a "half pipe" for writing. - */ -bool Pipe::createWriter(unsigned long handle) -{ - mWriteHandle = handle; - assert(mReadHandle == kInvalidHandle); - return true; -} - -/* - * Return "true" if create() has been called successfully. - */ -bool Pipe::isCreated(void) -{ - // one or the other should be open - return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); -} - - -/* - * Read data from the pipe. - * - * For Linux and Darwin, just call read(). For Windows, implement - * non-blocking reads by calling PeekNamedPipe first. - */ -int Pipe::read(void* buf, int count) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail = count; - DWORD bytesRead; - - if (mReadNonBlocking) { - // use PeekNamedPipe to adjust read count expectations - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return -1; - } - - if (totalBytesAvail == 0) - return 0; - } - - if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, - NULL)) - { - DWORD err = GetLastError(); - if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) - return 0; - LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); - return -1; - } - - return (int) bytesRead; -#else - int cc; - cc = ::read(mReadHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Write data to the pipe. - * - * POSIX systems are trivial, Windows uses a different call and doesn't - * handle non-blocking writes. - * - * If we add non-blocking support here, we probably want to make it an - * all-or-nothing write. - * - * DO NOT use LOG() here, we could be writing a log message. - */ -int Pipe::write(const void* buf, int count) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD bytesWritten; - - if (mWriteNonBlocking) { - // BUG: can't use PeekNamedPipe() to get the amount of space - // left. Looks like we need to use "overlapped I/O" functions. - // I just don't care that much. - } - - if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { - // can't LOG, use stderr - fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); - return -1; - } - - return (int) bytesWritten; -#else - int cc; - cc = ::write(mWriteHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Figure out if there is data available on the read fd. - * - * We return "true" on error because we want the caller to try to read - * from the pipe. They'll notice the read failure and do something - * appropriate. - */ -bool Pipe::readReady(void) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail; - - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return true; - } - - return (totalBytesAvail != 0); -#else - errno = 0; - fd_set readfds; - struct timeval tv = { 0, 0 }; - int cc; - - FD_ZERO(&readfds); - FD_SET(mReadHandle, &readfds); - - cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); - if (cc < 0) { - LOG(LOG_ERROR, "pipe", "select() failed\n"); - return true; - } else if (cc == 0) { - /* timed out, nothing available */ - return false; - } else if (cc == 1) { - /* our fd is ready */ - return true; - } else { - LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); - return true; - } -#endif -} - -/* - * Enable or disable non-blocking mode for the read descriptor. - * - * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to - * actually be in non-blocking mode. If this matters -- i.e. you're not - * using a select() call -- put a call to readReady() in front of the - * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for - * Darwin. - */ -bool Pipe::setReadNonBlocking(bool val) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); - return false; - } -#endif - - mReadNonBlocking = val; - return true; -} - -/* - * Enable or disable non-blocking mode for the write descriptor. - * - * As with setReadNonBlocking(), this does not work on the Mac. - */ -bool Pipe::setWriteNonBlocking(bool val) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't get flags for pipe write fd (errno=%d)\n", - errno); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't set flags for pipe write fd (errno=%d)\n", - errno); - return false; - } -#endif - - mWriteNonBlocking = val; - return true; -} - -/* - * Specify whether a file descriptor can be inherited by a child process. - * Under Linux this means setting the close-on-exec flag, under Windows - * this is SetHandleInformation(HANDLE_FLAG_INHERIT). - */ -bool Pipe::disallowReadInherit(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} -bool Pipe::disallowWriteInherit(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} - -/* - * Close read descriptor. - */ -bool Pipe::closeRead(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) { - LOG(LOG_WARN, "pipe", "failed closing read handle\n"); - return false; - } - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing read fd\n"); - return false; - } - } -#endif - mReadHandle = kInvalidHandle; - return true; -} - -/* - * Close write descriptor. - */ -bool Pipe::closeWrite(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mWriteHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mWriteHandle)) { - LOG(LOG_WARN, "pipe", "failed closing write handle\n"); - return false; - } - } -#else - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing write fd\n"); - return false; - } - } -#endif - mWriteHandle = kInvalidHandle; - return true; -} - -/* - * Get the read handle. - */ -unsigned long Pipe::getReadHandle(void) -{ - assert(mReadHandle != kInvalidHandle); - - return mReadHandle; -} - -/* - * Get the write handle. - */ -unsigned long Pipe::getWriteHandle(void) -{ - assert(mWriteHandle != kInvalidHandle); - - return mWriteHandle; -} - diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 0831f4a..872b2bc 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1740,7 +1740,11 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain package for resource number 0x%08x", resID); + } return false; } if (t < 0) { @@ -1786,7 +1790,11 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting value for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain package for resource number 0x%08x", resID); + } return BAD_INDEX; } if (t < 0) { @@ -3284,7 +3292,16 @@ bool ResTable::collectString(String16* outString, break; } if (c == '\'' && (quoted == 0 || quoted == '\'')) { - break; + /* + * In practice, when people write ' instead of \' + * in a string, they are doing it by accident + * instead of really meaning to use ' as a quoting + * character. Warn them so they don't lose it. + */ + if (outErrorMsg) { + *outErrorMsg = "Apostrophe not preceded by \\"; + } + return false; } } p++; diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp deleted file mode 100644 index 51509a3..0000000 --- a/libs/utils/Socket.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// - -#ifdef HAVE_WINSOCK -// This needs to come first, or Cygwin gets concerned about a potential -// clash between WinSock and <sys/types.h>. -# include <winsock2.h> -#endif - -#include <utils/Socket.h> -#include <utils/inet_address.h> -#include <utils/Log.h> -#include <utils/Timers.h> - -#ifndef HAVE_WINSOCK -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -# include <arpa/inet.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <assert.h> - -using namespace android; - - -/* - * =========================================================================== - * Socket - * =========================================================================== - */ - -#ifndef INVALID_SOCKET -# define INVALID_SOCKET (-1) -#endif -#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) - -/*static*/ bool Socket::mBootInitialized = false; - -/* - * Extract system-dependent error code. - */ -static inline int getSocketError(void) { -#ifdef HAVE_WINSOCK - return WSAGetLastError(); -#else - return errno; -#endif -} - -/* - * One-time initialization for socket code. - */ -/*static*/ bool Socket::bootInit(void) -{ -#ifdef HAVE_WINSOCK - WSADATA wsaData; - int err; - - err = WSAStartup(MAKEWORD(2, 0), &wsaData); - if (err != 0) { - LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); - return false; - } - - LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", - LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); -#endif - - mBootInitialized = true; - return true; -} - -/* - * One-time shutdown for socket code. - */ -/*static*/ void Socket::finalShutdown(void) -{ -#ifdef HAVE_WINSOCK - WSACleanup(); -#endif - mBootInitialized = false; -} - - -/* - * Simple constructor. Allow the application to create us and then make - * bind/connect calls. - */ -Socket::Socket(void) - : mSock(UNDEF_SOCKET) -{ - if (!mBootInitialized) - LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); -} - -/* - * Destructor. Closes the socket and resets our storage. - */ -Socket::~Socket(void) -{ - close(); -} - - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const char* host, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(host, port)) - return -1; - - //return doConnect(sockAddr); - int foo; - foo = doConnect(sockAddr); - return foo; -} - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const InetAddress* addr, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(addr, port)) - return -1; - - return doConnect(sockAddr); -} - -/* - * Finish creating a socket by connecting to the remote host. - * - * Returns 0 on success. - */ -int Socket::doConnect(const InetSocketAddress& sockAddr) -{ -#ifdef HAVE_WINSOCK - SOCKET sock; -#else - int sock; -#endif - const InetAddress* addr = sockAddr.getAddress(); - int port = sockAddr.getPort(); - struct sockaddr_in inaddr; - DurationTimer connectTimer; - - assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); - memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); - inaddr.sin_port = htons(port); - - //fprintf(stderr, "--- connecting to %s:%d\n", - // sockAddr.getHostName(), port); - - sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) { - int err = getSocketError(); - LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); - return (err != 0) ? err : -1; - } - - connectTimer.start(); - - if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { - int err = getSocketError(); - LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", - sockAddr.getHostName(), port, err); - return (err != 0) ? err : -1; - } - - connectTimer.stop(); - if ((long) connectTimer.durationUsecs() > 100000) { - LOG(LOG_INFO, "socket", - "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), - port, ((long) connectTimer.durationUsecs()) / 1000000.0); - } - - mSock = (unsigned long) sock; - LOG(LOG_VERBOSE, "socket", - "--- connected to %s:%d\n", sockAddr.getHostName(), port); - return 0; -} - - -/* - * Close the socket if it needs closing. - */ -bool Socket::close(void) -{ - if (mSock != UNDEF_SOCKET) { - //fprintf(stderr, "--- closing socket %lu\n", mSock); -#ifdef HAVE_WINSOCK - if (::closesocket((SOCKET) mSock) != 0) - return false; -#else - if (::close((int) mSock) != 0) - return false; -#endif - } - - mSock = UNDEF_SOCKET; - - return true; -} - -/* - * Read data from socket. - * - * Standard semantics: read up to "len" bytes into "buf". Returns the - * number of bytes read, or less than zero on error. - */ -int Socket::read(void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = recv(sock, (char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - -/* - * Write data to a socket. - * - * Standard semantics: write up to "len" bytes into "buf". Returns the - * number of bytes written, or less than zero on error. - */ -int Socket::write(const void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = send(sock, (const char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - - -/* - * =========================================================================== - * Socket tests - * =========================================================================== - */ - -/* - * Read all data from the socket. The data is read into a buffer that - * expands as needed. - * - * On exit, the buffer is returned, and the length of the data is stored - * in "*sz". A null byte is added to the end, but is not included in - * the length. - */ -static char* socketReadAll(const Socket& s, int *sz) -{ - int max, r; - char *data, *ptr, *tmp; - - data = (char*) malloc(max = 32768); - if (data == NULL) - return NULL; - - ptr = data; - - for (;;) { - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max *= 2); - if(tmp == 0) { - free(data); - return 0; - } - } - r = s.read(ptr, max - (ptr - data)); - if (r == 0) - break; - if (r < 0) { - LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); - break; - } - ptr += r; - } - - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max + 1); - if (tmp == NULL) { - free(data); - return NULL; - } - } - *ptr = '\0'; - *sz = (ptr - data); - return data; -} - -/* - * Exercise the Socket class. - */ -void android::TestSockets(void) -{ - printf("----- SOCKET TEST ------\n"); - Socket::bootInit(); - - char* buf = NULL; - int len, cc; - const char* kTestStr = - "GET / HTTP/1.0\n" - "Connection: close\n" - "\n"; - - Socket sock; - if (sock.connect("www.google.com", 80) != 0) { - fprintf(stderr, "socket connected failed\n"); - goto bail; - } - - cc = sock.write(kTestStr, strlen(kTestStr)); - if (cc != (int) strlen(kTestStr)) { - fprintf(stderr, "write failed, res=%d\n", cc); - goto bail; - } - buf = socketReadAll(sock, &len); - - printf("GOT '%s'\n", buf); - -bail: - sock.close(); - free(buf); -} - diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index 93f7e4f..4dfa578 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -20,7 +20,6 @@ #include <private/utils/Static.h> #include <utils/BufferedTextOutput.h> -#include <utils/IPCThreadState.h> #include <utils/Log.h> namespace android { @@ -87,34 +86,4 @@ TextOutput& alog(gLogTextOutput); TextOutput& aout(gStdoutTextOutput); TextOutput& aerr(gStderrTextOutput); -#ifndef LIBUTILS_NATIVE - -// ------------ ProcessState.cpp - -Mutex gProcessMutex; -sp<ProcessState> gProcess; - -class LibUtilsIPCtStatics -{ -public: - LibUtilsIPCtStatics() - { - } - - ~LibUtilsIPCtStatics() - { - IPCThreadState::shutdown(); - } -}; - -static LibUtilsIPCtStatics gIPCStatics; - -// ------------ ServiceManager.cpp - -Mutex gDefaultServiceManagerLock; -sp<IServiceManager> gDefaultServiceManager; -sp<IPermissionController> gPermissionController; - -#endif - } // namespace android diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp new file mode 100644 index 0000000..aa42d68 --- /dev/null +++ b/libs/utils/StringArray.cpp @@ -0,0 +1,113 @@ +/* + * 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// + +#include <stdlib.h> +#include <string.h> + +#include <utils/StringArray.h> + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +StringArray::StringArray() + : mMax(0), mCurrent(0), mArray(NULL) +{ +} + +StringArray:: ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; +} + +// +// Add a string. A copy of the string is made. +// +bool StringArray::push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; +} + +// +// Delete an entry. +// +void StringArray::erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; +} + +// +// Sort the array. +// +void StringArray::sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); +} + +// +// Pass this to the sort routine to do an ascending alphabetical sort. +// +int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); +} + +// +// Set entry N to specified string. +// [should use operator[] here] +// +void StringArray::setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); +} + + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp index cebee99..e04823d 100644 --- a/libs/utils/TextOutput.cpp +++ b/libs/utils/TextOutput.cpp @@ -22,9 +22,17 @@ #include <stdlib.h> #include <string.h> +namespace android { + // --------------------------------------------------------------------------- -namespace android { +TextOutput::TextOutput() { +} + +TextOutput::~TextOutput() { +} + +// --------------------------------------------------------------------------- TextOutput& operator<<(TextOutput& to, bool val) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 9287c0b..ec3db09 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -38,10 +38,6 @@ # define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW #endif -#if defined(HAVE_FUTEX) -#include <private/utils/futex_synchro.h> -#endif - #if defined(HAVE_PRCTL) #include <sys/prctl.h> #endif @@ -56,10 +52,6 @@ using namespace android; // ---------------------------------------------------------------------------- #if defined(HAVE_PTHREADS) -#if 0 -#pragma mark - -#pragma mark PTHREAD -#endif // ---------------------------------------------------------------------------- /* @@ -163,10 +155,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#pragma mark WIN32_THREADS -#endif // ---------------------------------------------------------------------------- /* @@ -252,11 +240,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Common Thread functions -#endif - int androidCreateThread(android_thread_func_t fn, void* arg) { return createThreadEtc(fn, arg); @@ -294,112 +277,23 @@ namespace android { * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Mutex -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) -/* - * Simple pthread wrapper. - */ +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h +#elif defined(HAVE_WIN32_THREADS) Mutex::Mutex() { - _init(); -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - _init(); -} - -void Mutex::_init() -{ - pthread_mutex_t* pMutex = new pthread_mutex_t; - pthread_mutex_init(pMutex, NULL); - mState = pMutex; -} - -Mutex::~Mutex() -{ - delete (pthread_mutex_t*) mState; -} - -status_t Mutex::lock() -{ - int res; - while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - pthread_mutex_unlock((pthread_mutex_t*) mState); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif + HANDLE hMutex; -#define STATE ((futex_mutex_t*) (&mState)) + assert(sizeof(hMutex) == sizeof(mState)); -Mutex::Mutex() -{ - _init(); + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; } Mutex::Mutex(const char* name) { - _init(); -} - -void -Mutex::_init() -{ - futex_mutex_init(STATE); -} - -Mutex::~Mutex() -{ -} - -status_t Mutex::lock() -{ - int res; - while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - futex_mutex_unlock(STATE); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=futex_mutex_trylock(STATE)) == EINTR) ; - return -res; -} -#undef STATE - -#elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif - -Mutex::Mutex() -{ + // XXX: name not used for now HANDLE hMutex; assert(sizeof(hMutex) == sizeof(mState)); @@ -408,11 +302,13 @@ Mutex::Mutex() mState = (void*) hMutex; } -Mutex::Mutex(const char* name) +Mutex::Mutex(int type, const char* name) { - // XXX: name not used for now + // XXX: type and name not used for now HANDLE hMutex; + assert(sizeof(hMutex) == sizeof(mState)); + hMutex = CreateMutex(NULL, FALSE, NULL); mState = (void*) hMutex; } @@ -456,161 +352,9 @@ status_t Mutex::tryLock() * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Condition -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - pthread_cond_t* pCond = new pthread_cond_t; - - pthread_cond_init(pCond, NULL); - mState = pCond; -} - -/* - * Destructor. - */ -Condition::~Condition() -{ - pthread_cond_destroy((pthread_cond_t*) mState); - delete (pthread_cond_t*) mState; -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int cc; - while ((cc = pthread_cond_wait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState)) == EINTR) ; - return -cc; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - assert(mutex.mState != NULL); - - struct timespec ts; - ts.tv_sec = abstime/1000000000; - ts.tv_nsec = abstime-(ts.tv_sec*1000000000); - - int cc; - while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; - return -cc; -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - pthread_cond_signal((pthread_cond_t*) mState); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - pthread_cond_broadcast((pthread_cond_t*) mState); -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_cond_t*) (&mState)) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - futex_cond_init(STATE); -} - -/* - * Destructor. - */ -Condition::~Condition() -{ -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int res; - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; - - return -res; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - nsecs_t reltime = abstime - systemTime(); - if (reltime <= 0) return true; - return waitRelative(mutex, reltime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - assert(mutex.mState != NULL); - int res; - unsigned msec = ns2ms(reltime); - if(msec == 0) - return true; - // This code will not time out at the correct time if interrupted by signals - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; - return res; -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - futex_cond_signal(STATE); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - futex_cond_broadcast(STATE); -} - -#undef STATE - +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif /* * Windows doesn't have a condition variable solution. It's possible @@ -753,17 +497,13 @@ status_t Condition::wait(Mutex& mutex) return ((WinCondition*)mState)->wait(condState, hMutex, NULL); } -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { WinCondition* condState = (WinCondition*) mState; HANDLE hMutex = (HANDLE) mutex.mState; + nsecs_t absTime = systemTime()+reltime; - return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); + return ((WinCondition*)mState)->wait(condState, hMutex, &absTime); } /* @@ -841,11 +581,6 @@ void Condition::broadcast() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Thread::Thread -#endif - /* * This is our thread object! */ @@ -920,6 +655,11 @@ int Thread::_threadLoop(void* user) wp<Thread> weak(strong); self->mHoldSelf.clear(); +#if HAVE_ANDROID_OS + // this is very useful for debugging with gdb + self->mTid = gettid(); +#endif + bool first = true; do { @@ -950,7 +690,7 @@ int Thread::_threadLoop(void* user) self->mExitPending = true; self->mLock.lock(); self->mRunning = false; - self->mThreadExitedCondition.signal(); + self->mThreadExitedCondition.broadcast(); self->mLock.unlock(); break; } @@ -958,7 +698,7 @@ int Thread::_threadLoop(void* user) // Release our strong reference, to let a chance to the thread // to die a peaceful death. strong.clear(); - // And immediately, reacquire a strong reference for the next loop + // And immediately, re-acquire a strong reference for the next loop strong = weak.promote(); } while(strong != 0); diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp deleted file mode 100644 index 835480d..0000000 --- a/libs/utils/TimerProbe.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 <utils/TimerProbe.h> - -#if ENABLE_TIMER_PROBE - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "time" - -namespace android { - -Vector<TimerProbe::Bucket> TimerProbe::gBuckets; -TimerProbe* TimerProbe::gExecuteChain; -int TimerProbe::gIndent; -timespec TimerProbe::gRealBase; - -TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) -{ - mNext = gExecuteChain; - gExecuteChain = this; - mIndent = gIndent; - gIndent += 1; - if (mIndent > 0) { - if (*slot == 0) { - int count = gBuckets.add(); - *slot = count; - Bucket& bucket = gBuckets.editItemAt(count); - memset(&bucket, 0, sizeof(Bucket)); - bucket.mTag = tag; - bucket.mSlotPtr = slot; - bucket.mIndent = mIndent; - } - mBucket = *slot; - } - clock_gettime(CLOCK_REALTIME, &mRealStart); - if (gRealBase.tv_sec == 0) - gRealBase = mRealStart; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); -} - -void TimerProbe::end() -{ - timespec realEnd, pEnd, tEnd; - clock_gettime(CLOCK_REALTIME, &realEnd); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); - print(realEnd, pEnd, tEnd); - mTag = NULL; -} - -TimerProbe::~TimerProbe() -{ - if (mTag != NULL) - end(); - gExecuteChain = mNext; - gIndent--; -} - - -uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) -{ - int sec = end.tv_sec - start.tv_sec; - int nsec = end.tv_nsec - start.tv_nsec; - if (nsec < 0) { - sec--; - nsec += 1000000000; - } - return sec * 1000000 + nsec / 1000; -} - -void TimerProbe::print(const timespec& r, const timespec& p, - const timespec& t) const -{ - uint32_t es = ElapsedTime(gRealBase, mRealStart); - uint32_t er = ElapsedTime(mRealStart, r); - uint32_t ep = ElapsedTime(mPStart, p); - uint32_t et = ElapsedTime(mTStart, t); - if (mIndent > 0) { - Bucket& bucket = gBuckets.editItemAt(mBucket); - if (bucket.mStart == 0) - bucket.mStart = es; - bucket.mReal += er; - bucket.mProcess += ep; - bucket.mThread += et; - bucket.mCount++; - return; - } - int index = 0; - int buckets = gBuckets.size(); - int count = 1; - const char* tag = mTag; - int indent = mIndent; - do { - LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", - tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, - er, ep, ep * 100 / er, et, et * 100 / er); - if (index >= buckets) - break; - Bucket& bucket = gBuckets.editItemAt(index); - count = bucket.mCount; - es = bucket.mStart; - er = bucket.mReal; - ep = bucket.mProcess; - et = bucket.mThread; - tag = bucket.mTag; - indent = bucket.mIndent; - *bucket.mSlotPtr = 0; - } while (++index); // always true - gBuckets.clear(); -} - -}; // namespace android - -#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 2abc811..784f035 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -18,7 +18,6 @@ // Timer functions. // #include <utils/Timers.h> -#include <utils/ported.h> // may need usleep #include <utils/Log.h> #include <stdlib.h> @@ -54,130 +53,6 @@ nsecs_t systemTime(int clock) #endif } -//#define MONITOR_USLEEP - -/* - * Sleep long enough that we'll wake up "interval" milliseconds after - * the previous snooze. - * - * The "nextTick" argument is updated on each call, and should be passed - * in every time. Set its fields to zero on the first call. - * - * Returns the #of intervals we have overslept, which will be zero if we're - * on time. [Currently just returns 0 or 1.] - */ -int sleepForInterval(long interval, struct timeval* pNextTick) -{ - struct timeval now; - long long timeBeforeNext; - long sleepTime = 0; - bool overSlept = false; - //int usleepBias = 0; - -#ifdef USLEEP_BIAS - /* - * Linux likes to add 9000ms or so. - * [not using this for now] - */ - //usleepBias = USLEEP_BIAS; -#endif - - gettimeofday(&now, NULL); - - if (pNextTick->tv_sec == 0) { - /* special-case for first time through */ - *pNextTick = now; - sleepTime = interval; - android::DurationTimer::addToTimeval(pNextTick, interval); - } else { - /* - * Compute how much time there is before the next tick. If this - * value is negative, we've run over. If we've run over a little - * bit we can shorten the next frame to keep the pace steady, but - * if we've dramatically overshot we need to re-sync. - */ - timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); - //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // (long) timeBeforeNext); - if (timeBeforeNext < -interval) { - /* way over */ - overSlept = true; - sleepTime = 0; - *pNextTick = now; - } else if (timeBeforeNext <= 0) { - /* slightly over, keep the pace steady */ - overSlept = true; - sleepTime = 0; - } else if (timeBeforeNext <= interval) { - /* right on schedule */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { - /* sleep call returned early; do a longer sleep this time */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval) { - /* we went back in time -- somebody updated system clock? */ - /* (could also be a *seriously* broken usleep()) */ - LOG(LOG_DEBUG, "", - " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); - sleepTime = 0; - *pNextTick = now; - } - android::DurationTimer::addToTimeval(pNextTick, interval); - } - //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // sleepTime); - - /* - * Sleep for the designated period of time. - * - * Linux tends to sleep for longer than requested, often by 17-18ms. - * MinGW tends to sleep for less than requested, by as much as 14ms, - * but occasionally oversleeps for 40+ms (looks like some external - * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. - * - * If you start the MinGW version, and then launch the Cygwin version, - * the MinGW clock becomes more erratic. Not entirely sure why. - * - * (There's a lot of stuff here; it's really just a usleep() call with - * a bunch of instrumentation.) - */ - if (sleepTime > 0) { -#if defined(MONITOR_USLEEP) - struct timeval before, after; - long long actual; - - gettimeofday(&before, NULL); - usleep((long) sleepTime); - gettimeofday(&after, NULL); - - /* check usleep() accuracy; default Linux threads are pretty sloppy */ - actual = android::DurationTimer::subtractTimevals(&after, &before); - if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || - (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) - { - LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, - (long) actual); - } -#else -#ifdef HAVE_WIN32_THREADS - Sleep( sleepTime/1000 ); -#else - usleep((long) sleepTime); -#endif -#endif - } - - //printf("slept %d\n", sleepTime); - - if (overSlept) - return 1; // close enough - else - return 0; -} - - /* * =========================================================================== * DurationTimer diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp deleted file mode 100644 index 96f9fc4..0000000 --- a/libs/utils/ZipEntry.cpp +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright (C) 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. - */ - -// -// Access to entries in a Zip archive. -// - -#define LOG_TAG "zip" - -#include <utils/ZipEntry.h> -#include <utils/Log.h> - -#include <stdio.h> -#include <string.h> -#include <assert.h> - -using namespace android; - -/* - * Initialize a new ZipEntry structure from a FILE* positioned at a - * CentralDirectoryEntry. - * - * On exit, the file pointer will be at the start of the next CDE or - * at the EOCD. - */ -status_t ZipEntry::initFromCDE(FILE* fp) -{ - status_t result; - long posn; - bool hasDD; - - //LOGV("initFromCDE ---\n"); - - /* read the CDE */ - result = mCDE.read(fp); - if (result != NO_ERROR) { - LOGD("mCDE.read failed\n"); - return result; - } - - //mCDE.dump(); - - /* using the info in the CDE, go load up the LFH */ - posn = ftell(fp); - if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { - LOGD("local header seek failed (%ld)\n", - mCDE.mLocalHeaderRelOffset); - return UNKNOWN_ERROR; - } - - result = mLFH.read(fp); - if (result != NO_ERROR) { - LOGD("mLFH.read failed\n"); - return result; - } - - if (fseek(fp, posn, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - //mLFH.dump(); - - /* - * We *might* need to read the Data Descriptor at this point and - * integrate it into the LFH. If this bit is set, the CRC-32, - * compressed size, and uncompressed size will be zero. In practice - * these seem to be rare. - */ - hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; - if (hasDD) { - // do something clever - //LOGD("+++ has data descriptor\n"); - } - - /* - * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" - * flag is set, because the LFH is incomplete. (Not a problem, since we - * prefer the CDE values.) - */ - if (!hasDD && !compareHeaders()) { - LOGW("WARNING: header mismatch\n"); - // keep going? - } - - /* - * If the mVersionToExtract is greater than 20, we may have an - * issue unpacking the record -- could be encrypted, compressed - * with something we don't support, or use Zip64 extensions. We - * can defer worrying about that to when we're extracting data. - */ - - return NO_ERROR; -} - -/* - * Initialize a new entry. Pass in the file name and an optional comment. - * - * Initializes the CDE and the LFH. - */ -void ZipEntry::initNew(const char* fileName, const char* comment) -{ - assert(fileName != NULL && *fileName != '\0'); // name required - - /* most fields are properly initialized by constructor */ - mCDE.mVersionMadeBy = kDefaultMadeBy; - mCDE.mVersionToExtract = kDefaultVersion; - mCDE.mCompressionMethod = kCompressStored; - mCDE.mFileNameLength = strlen(fileName); - if (comment != NULL) - mCDE.mFileCommentLength = strlen(comment); - mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - strcpy((char*) mCDE.mFileName, fileName); - } - if (mCDE.mFileCommentLength > 0) { - /* TODO: stop assuming null-terminated ASCII here? */ - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - strcpy((char*) mCDE.mFileComment, comment); - } - - copyCDEtoLFH(); -} - -/* - * Initialize a new entry, starting with the ZipEntry from a different - * archive. - * - * Initializes the CDE and the LFH. - */ -status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, - const ZipEntry* pEntry) -{ - /* - * Copy everything in the CDE over, then fix up the hairy bits. - */ - memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - if (mCDE.mFileName == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); - } - if (mCDE.mFileCommentLength > 0) { - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - if (mCDE.mFileComment == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); - } - if (mCDE.mExtraFieldLength > 0) { - /* we null-terminate this, though it may not be a string */ - mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; - if (mCDE.mExtraField == NULL) - return NO_MEMORY; - memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, - mCDE.mExtraFieldLength+1); - } - - /* construct the LFH from the CDE */ - copyCDEtoLFH(); - - /* - * The LFH "extra" field is independent of the CDE "extra", so we - * handle it here. - */ - assert(mLFH.mExtraField == NULL); - mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; - if (mLFH.mExtraFieldLength > 0) { - mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; - if (mLFH.mExtraField == NULL) - return NO_MEMORY; - memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, - mLFH.mExtraFieldLength+1); - } - - return NO_ERROR; -} - -/* - * Insert pad bytes in the LFH by tweaking the "extra" field. This will - * potentially confuse something that put "extra" data in here earlier, - * but I can't find an actual problem. - */ -status_t ZipEntry::addPadding(int padding) -{ - if (padding <= 0) - return INVALID_OPERATION; - - //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", - // padding, mLFH.mExtraFieldLength, mCDE.mFileName); - - if (mLFH.mExtraFieldLength > 0) { - /* extend existing field */ - unsigned char* newExtra; - - newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; - if (newExtra == NULL) - return NO_MEMORY; - memset(newExtra + mLFH.mExtraFieldLength, 0, padding); - memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); - - delete[] mLFH.mExtraField; - mLFH.mExtraField = newExtra; - mLFH.mExtraFieldLength += padding; - } else { - /* create new field */ - mLFH.mExtraField = new unsigned char[padding]; - memset(mLFH.mExtraField, 0, padding); - mLFH.mExtraFieldLength = padding; - } - - return NO_ERROR; -} - -/* - * Set the fields in the LFH equal to the corresponding fields in the CDE. - * - * This does not touch the LFH "extra" field. - */ -void ZipEntry::copyCDEtoLFH(void) -{ - mLFH.mVersionToExtract = mCDE.mVersionToExtract; - mLFH.mGPBitFlag = mCDE.mGPBitFlag; - mLFH.mCompressionMethod = mCDE.mCompressionMethod; - mLFH.mLastModFileTime = mCDE.mLastModFileTime; - mLFH.mLastModFileDate = mCDE.mLastModFileDate; - mLFH.mCRC32 = mCDE.mCRC32; - mLFH.mCompressedSize = mCDE.mCompressedSize; - mLFH.mUncompressedSize = mCDE.mUncompressedSize; - mLFH.mFileNameLength = mCDE.mFileNameLength; - // the "extra field" is independent - - delete[] mLFH.mFileName; - if (mLFH.mFileNameLength > 0) { - mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; - strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); - } else { - mLFH.mFileName = NULL; - } -} - -/* - * Set some information about a file after we add it. - */ -void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod) -{ - mCDE.mCompressionMethod = compressionMethod; - mCDE.mCRC32 = crc32; - mCDE.mCompressedSize = compLen; - mCDE.mUncompressedSize = uncompLen; - mCDE.mCompressionMethod = compressionMethod; - if (compressionMethod == kCompressDeflated) { - mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used - } - copyCDEtoLFH(); -} - -/* - * See if the data in mCDE and mLFH match up. This is mostly useful for - * debugging these classes, but it can be used to identify damaged - * archives. - * - * Returns "false" if they differ. - */ -bool ZipEntry::compareHeaders(void) const -{ - if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { - LOGV("cmp: VersionToExtract\n"); - return false; - } - if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { - LOGV("cmp: GPBitFlag\n"); - return false; - } - if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { - LOGV("cmp: CompressionMethod\n"); - return false; - } - if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { - LOGV("cmp: LastModFileTime\n"); - return false; - } - if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { - LOGV("cmp: LastModFileDate\n"); - return false; - } - if (mCDE.mCRC32 != mLFH.mCRC32) { - LOGV("cmp: CRC32\n"); - return false; - } - if (mCDE.mCompressedSize != mLFH.mCompressedSize) { - LOGV("cmp: CompressedSize\n"); - return false; - } - if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { - LOGV("cmp: UncompressedSize\n"); - return false; - } - if (mCDE.mFileNameLength != mLFH.mFileNameLength) { - LOGV("cmp: FileNameLength\n"); - return false; - } -#if 0 // this seems to be used for padding, not real data - if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { - LOGV("cmp: ExtraFieldLength\n"); - return false; - } -#endif - if (mCDE.mFileName != NULL) { - if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { - LOGV("cmp: FileName\n"); - return false; - } - } - - return true; -} - - -/* - * Convert the DOS date/time stamp into a UNIX time stamp. - */ -time_t ZipEntry::getModWhen(void) const -{ - struct tm parts; - - parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; - parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; - parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; - parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); - parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; - parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; - parts.tm_wday = parts.tm_yday = 0; - parts.tm_isdst = -1; // DST info "not available" - - return mktime(&parts); -} - -/* - * Set the CDE/LFH timestamp from UNIX time. - */ -void ZipEntry::setModWhen(time_t when) -{ -#ifdef HAVE_LOCALTIME_R - struct tm tmResult; -#endif - time_t even; - unsigned short zdate, ztime; - - struct tm* ptm; - - /* round up to an even number of seconds */ - even = (time_t)(((unsigned long)(when) + 1) & (~1)); - - /* expand */ -#ifdef HAVE_LOCALTIME_R - ptm = localtime_r(&even, &tmResult); -#else - ptm = localtime(&even); -#endif - - int year; - year = ptm->tm_year; - if (year < 80) - year = 80; - - zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; - - mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; - mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; -} - - -/* - * =========================================================================== - * ZipEntry::LocalFileHeader - * =========================================================================== - */ - -/* - * Read a local file header. - * - * On entry, "fp" points to the signature at the start of the header. - * On exit, "fp" points to the start of data. - */ -status_t ZipEntry::LocalFileHeader::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kLFHLen]; - - assert(mFileName == NULL); - assert(mExtraField == NULL); - - if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); - mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); - - // TODO: validate sizes - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* grab extra field */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a local file header. - */ -status_t ZipEntry::LocalFileHeader::write(FILE* fp) -{ - unsigned char buf[kLFHLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x0e], mCRC32); - ZipEntry::putLongLE(&buf[0x12], mCompressedSize); - ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); - - if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Dump the contents of a LocalFileHeader object. - */ -void ZipEntry::LocalFileHeader::dump(void) const -{ - LOGD(" LocalFileHeader contents:\n"); - LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u\n", - mFileNameLength, mExtraFieldLength); - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); -} - - -/* - * =========================================================================== - * ZipEntry::CentralDirEntry - * =========================================================================== - */ - -/* - * Read the central dir entry that appears next in the file. - * - * On entry, "fp" should be positioned on the signature bytes for the - * entry. On exit, "fp" will point at the signature word for the next - * entry or for the EOCD. - */ -status_t ZipEntry::CentralDirEntry::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kCDELen]; - - /* no re-use */ - assert(mFileName == NULL); - assert(mExtraField == NULL); - assert(mFileComment == NULL); - - if (fread(buf, 1, kCDELen, fp) != kCDELen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("Whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); - mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); - mCRC32 = ZipEntry::getLongLE(&buf[0x10]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); - mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); - mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); - mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); - mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); - mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); - - // TODO: validate sizes and offsets - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* read "extra field" */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - - - /* grab comment, if any */ - if (mFileCommentLength != 0) { - mFileComment = new unsigned char[mFileCommentLength+1]; - if (mFileComment == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - { - result = UNKNOWN_ERROR; - goto bail; - } - mFileComment[mFileCommentLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a central dir entry. - */ -status_t ZipEntry::CentralDirEntry::write(FILE* fp) -{ - unsigned char buf[kCDELen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); - ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x10], mCRC32); - ZipEntry::putLongLE(&buf[0x14], mCompressedSize); - ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); - ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); - ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); - ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); - ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); - ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); - - if (fwrite(buf, 1, kCDELen, fp) != kCDELen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - /* write comment */ - if (mFileCommentLength != 0) { - if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of a CentralDirEntry object. - */ -void ZipEntry::CentralDirEntry::dump(void) const -{ - LOGD(" CentralDirEntry contents:\n"); - LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", - mFileNameLength, mExtraFieldLength, mFileCommentLength); - LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", - mDiskNumberStart, mInternalAttrs, mExternalAttrs, - mLocalHeaderRelOffset); - - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); - if (mFileComment != NULL) - LOGD(" comment: '%s'\n", mFileComment); -} - diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp deleted file mode 100644 index 6f27d17..0000000 --- a/libs/utils/ZipFile.cpp +++ /dev/null @@ -1,1296 +0,0 @@ -/* - * Copyright (C) 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. - */ - -// -// Access to Zip archives. -// - -#define LOG_TAG "zip" - -#include <utils/ZipFile.h> -#include <utils/ZipUtils.h> -#include <utils/Log.h> - -#include <zlib.h> -#define DEF_MEM_LEVEL 8 // normally in zutil.h? - -#include <memory.h> -#include <sys/stat.h> -#include <errno.h> -#include <assert.h> - -using namespace android; - -/* - * Some environments require the "b", some choke on it. - */ -#define FILE_OPEN_RO "rb" -#define FILE_OPEN_RW "r+b" -#define FILE_OPEN_RW_CREATE "w+b" - -/* should live somewhere else? */ -static status_t errnoToStatus(int err) -{ - if (err == ENOENT) - return NAME_NOT_FOUND; - else if (err == EACCES) - return PERMISSION_DENIED; - else - return UNKNOWN_ERROR; -} - -/* - * Open a file and parse its guts. - */ -status_t ZipFile::open(const char* zipFileName, int flags) -{ - bool newArchive = false; - - assert(mZipFp == NULL); // no reopen - - if ((flags & kOpenTruncate)) - flags |= kOpenCreate; // trunc implies create - - if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) - return INVALID_OPERATION; // not both - if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) - return INVALID_OPERATION; // not neither - if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) - return INVALID_OPERATION; // create requires write - - if (flags & kOpenTruncate) { - newArchive = true; - } else { - newArchive = (access(zipFileName, F_OK) != 0); - if (!(flags & kOpenCreate) && newArchive) { - /* not creating, must already exist */ - LOGD("File %s does not exist", zipFileName); - return NAME_NOT_FOUND; - } - } - - /* open the file */ - const char* openflags; - if (flags & kOpenReadWrite) { - if (newArchive) - openflags = FILE_OPEN_RW_CREATE; - else - openflags = FILE_OPEN_RW; - } else { - openflags = FILE_OPEN_RO; - } - mZipFp = fopen(zipFileName, openflags); - if (mZipFp == NULL) { - int err = errno; - LOGD("fopen failed: %d\n", err); - return errnoToStatus(err); - } - - status_t result; - if (!newArchive) { - /* - * Load the central directory. If that fails, then this probably - * isn't a Zip archive. - */ - result = readCentralDir(); - } else { - /* - * Newly-created. The EndOfCentralDir constructor actually - * sets everything to be the way we want it (all zeroes). We - * set mNeedCDRewrite so that we create *something* if the - * caller doesn't add any files. (We could also just unlink - * the file if it's brand new and nothing was added, but that's - * probably doing more than we really should -- the user might - * have a need for empty zip files.) - */ - mNeedCDRewrite = true; - result = NO_ERROR; - } - - if (flags & kOpenReadOnly) - mReadOnly = true; - else - assert(!mReadOnly); - - return result; -} - -/* - * Return the Nth entry in the archive. - */ -ZipEntry* ZipFile::getEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= (int) mEntries.size()) - return NULL; - - return mEntries[idx]; -} - -/* - * Find an entry by name. - */ -ZipEntry* ZipFile::getEntryByName(const char* fileName) const -{ - /* - * Do a stupid linear string-compare search. - * - * There are various ways to speed this up, especially since it's rare - * to intermingle changes to the archive with "get by name" calls. We - * don't want to sort the mEntries vector itself, however, because - * it's used to recreate the Central Directory. - * - * (Hash table works, parallel list of pointers in sorted order is good.) - */ - int idx; - - for (idx = mEntries.size()-1; idx >= 0; idx--) { - ZipEntry* pEntry = mEntries[idx]; - if (!pEntry->getDeleted() && - strcmp(fileName, pEntry->getFileName()) == 0) - { - return pEntry; - } - } - - return NULL; -} - -/* - * Empty the mEntries vector. - */ -void ZipFile::discardEntries(void) -{ - int count = mEntries.size(); - - while (--count >= 0) - delete mEntries[count]; - - mEntries.clear(); -} - - -/* - * Find the central directory and read the contents. - * - * The fun thing about ZIP archives is that they may or may not be - * readable from start to end. In some cases, notably for archives - * that were written to stdout, the only length information is in the - * central directory at the end of the file. - * - * Of course, the central directory can be followed by a variable-length - * comment field, so we have to scan through it backwards. The comment - * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff - * itself, plus apparently sometimes people throw random junk on the end - * just for the fun of it. - * - * This is all a little wobbly. If the wrong value ends up in the EOCD - * area, we're hosed. This appears to be the way that everbody handles - * it though, so we're in pretty good company if this fails. - */ -status_t ZipFile::readCentralDir(void) -{ - status_t result = NO_ERROR; - unsigned char* buf = NULL; - off_t fileLength, seekStart; - long readAmount; - int i; - - fseek(mZipFp, 0, SEEK_END); - fileLength = ftell(mZipFp); - rewind(mZipFp); - - /* too small to be a ZIP archive? */ - if (fileLength < EndOfCentralDir::kEOCDLen) { - LOGD("Length is %ld -- too small\n", (long)fileLength); - result = INVALID_OPERATION; - goto bail; - } - - buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; - if (buf == NULL) { - LOGD("Failure allocating %d bytes for EOCD search", - EndOfCentralDir::kMaxEOCDSearch); - result = NO_MEMORY; - goto bail; - } - - if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { - seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; - readAmount = EndOfCentralDir::kMaxEOCDSearch; - } else { - seekStart = 0; - readAmount = (long) fileLength; - } - if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { - LOGD("Failure seeking to end of zip at %ld", (long) seekStart); - result = UNKNOWN_ERROR; - goto bail; - } - - /* read the last part of the file into the buffer */ - if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { - LOGD("short file? wanted %ld\n", readAmount); - result = UNKNOWN_ERROR; - goto bail; - } - - /* find the end-of-central-dir magic */ - for (i = readAmount - 4; i >= 0; i--) { - if (buf[i] == 0x50 && - ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) - { - LOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - LOGD("EOCD not found, not Zip\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* extract eocd values */ - result = mEOCD.readBuf(buf + i, readAmount - i); - if (result != NO_ERROR) { - LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); - goto bail; - } - //mEOCD.dump(); - - if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || - mEOCD.mNumEntries != mEOCD.mTotalNumEntries) - { - LOGD("Archive spanning not supported\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* - * So far so good. "mCentralDirSize" is the size in bytes of the - * central directory, so we can just seek back that far to find it. - * We can also seek forward mCentralDirOffset bytes from the - * start of the file. - * - * We're not guaranteed to have the rest of the central dir in the - * buffer, nor are we guaranteed that the central dir will have any - * sort of convenient size. We need to skip to the start of it and - * read the header, then the other goodies. - * - * The only thing we really need right now is the file comment, which - * we're hoping to preserve. - */ - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - LOGD("Failure seeking to central dir offset %ld\n", - mEOCD.mCentralDirOffset); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Loop through and read the central dir entries. - */ - LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); - int entry; - for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { - ZipEntry* pEntry = new ZipEntry; - - result = pEntry->initFromCDE(mZipFp); - if (result != NO_ERROR) { - LOGD("initFromCDE failed\n"); - delete pEntry; - goto bail; - } - - mEntries.add(pEntry); - } - - - /* - * If all went well, we should now be back at the EOCD. - */ - { - unsigned char checkBuf[4]; - if (fread(checkBuf, 1, 4, mZipFp) != 4) { - LOGD("EOCD check read failed\n"); - result = INVALID_OPERATION; - goto bail; - } - if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { - LOGD("EOCD read check failed\n"); - result = UNKNOWN_ERROR; - goto bail; - } - LOGV("+++ EOCD read check passed\n"); - } - -bail: - delete[] buf; - return result; -} - - -/* - * Add a new file to the archive. - * - * This requires creating and populating a ZipEntry structure, and copying - * the data into the file at the appropriate position. The "appropriate - * position" is the current location of the central directory, which we - * casually overwrite (we can put it back later). - * - * If we were concerned about safety, we would want to make all changes - * in a temp file and then overwrite the original after everything was - * safely written. Not really a concern for us. - */ -status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result = NO_ERROR; - long lfhPosn, startPosn, endPosn, uncompressedLen; - FILE* inputFp = NULL; - unsigned long crc; - time_t modWhen; - - if (mReadOnly) - return INVALID_OPERATION; - - assert(compressionMethod == ZipEntry::kCompressDeflated || - compressionMethod == ZipEntry::kCompressStored); - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - /* make sure it doesn't already exist */ - if (getEntryByName(storageName) != NULL) - return ALREADY_EXISTS; - - if (!data) { - inputFp = fopen(fileName, FILE_OPEN_RO); - if (inputFp == NULL) - return errnoToStatus(errno); - } - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - pEntry->initNew(storageName, NULL); - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH, even though it's still mostly blank. We need it - * as a place-holder. In theory the LFH isn't necessary, but in - * practice some utilities demand it. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - startPosn = ftell(mZipFp); - - /* - * Copy the data in, possibly compressing it as we go. - */ - if (sourceType == ZipEntry::kCompressStored) { - if (compressionMethod == ZipEntry::kCompressDeflated) { - bool failed = false; - result = compressFpToFp(mZipFp, inputFp, data, size, &crc); - if (result != NO_ERROR) { - LOGD("compression failed, storing\n"); - failed = true; - } else { - /* - * Make sure it has compressed "enough". This probably ought - * to be set through an API call, but I don't expect our - * criteria to change over time. - */ - long src = inputFp ? ftell(inputFp) : size; - long dst = ftell(mZipFp) - startPosn; - if (dst + (dst / 10) > src) { - LOGD("insufficient compression (src=%ld dst=%ld), storing\n", - src, dst); - failed = true; - } - } - - if (failed) { - compressionMethod = ZipEntry::kCompressStored; - if (inputFp) rewind(inputFp); - fseek(mZipFp, startPosn, SEEK_SET); - /* fall through to kCompressStored case */ - } - } - /* handle "no compression" request, or failed compression from above */ - if (compressionMethod == ZipEntry::kCompressStored) { - if (inputFp) { - result = copyFpToFp(mZipFp, inputFp, &crc); - } else { - result = copyDataToFp(mZipFp, data, size, &crc); - } - if (result != NO_ERROR) { - // don't need to truncate; happens in CDE rewrite - LOGD("failed copying data in\n"); - goto bail; - } - } - - // currently seeked to end of file - uncompressedLen = inputFp ? ftell(inputFp) : size; - } else if (sourceType == ZipEntry::kCompressDeflated) { - /* we should support uncompressed-from-compressed, but it's not - * important right now */ - assert(compressionMethod == ZipEntry::kCompressDeflated); - - bool scanResult; - int method; - long compressedLen; - - scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, - &compressedLen, &crc); - if (!scanResult || method != ZipEntry::kCompressDeflated) { - LOGD("this isn't a deflated gzip file?"); - result = UNKNOWN_ERROR; - goto bail; - } - - result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); - if (result != NO_ERROR) { - LOGD("failed copying gzip data in\n"); - goto bail; - } - } else { - assert(false); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * We could write the "Data Descriptor", but there doesn't seem to - * be any point since we're going to go back and write the LFH. - * - * Update file offsets. - */ - endPosn = ftell(mZipFp); // seeked to end of compressed data - - /* - * Success! Fill out new values. - */ - pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, - compressionMethod); - modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); - pEntry->setModWhen(modWhen); - pEntry->setLFHOffset(lfhPosn); - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Go back and write the LFH. - */ - if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - pEntry->mLFH.write(mZipFp); - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - -bail: - if (inputFp != NULL) - fclose(inputFp); - delete pEntry; - return result; -} - -/* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ -status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result; - long lfhPosn, endPosn; - - if (mReadOnly) - return INVALID_OPERATION; - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - if (pEntry == NULL) { - result = NO_MEMORY; - goto bail; - } - - result = pEntry->initFromExternal(pSourceZip, pSourceEntry); - if (result != NO_ERROR) - goto bail; - if (padding != 0) { - result = pEntry->addPadding(padding); - if (result != NO_ERROR) - goto bail; - } - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH. Since we're not recompressing the data, we already - * have all of the fields filled out. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - - /* - * Copy the data over. - * - * If the "has data descriptor" flag is set, we want to copy the DD - * fields as well. This is a fixed-size area immediately following - * the data. - */ - if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) - { - result = UNKNOWN_ERROR; - goto bail; - } - - off_t copyLen; - copyLen = pSourceEntry->getCompressedLen(); - if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) - copyLen += ZipEntry::kDataDescriptorLen; - - if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) - != NO_ERROR) - { - LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Update file offsets. - */ - endPosn = ftell(mZipFp); - - /* - * Success! Fill out new values. - */ - pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - - result = NO_ERROR; - -bail: - delete pEntry; - return result; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data. - */ -status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (1) { - count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); - if (ferror(srcFp) || ferror(dstFp)) - return errnoToStatus(errno); - if (count == 0) - break; - - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "dstFp" will be seeked immediately past the data. - */ -status_t ZipFile::copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - if (size > 0) { - *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); - if (fwrite(data, 1, size, dstFp) != size) { - LOGD("fwrite %d bytes failed\n", (int) size); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy some of the bytes in "src" to "dst". - * - * If "pCRC32" is NULL, the CRC will not be computed. - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data just written. - */ -status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - if (pCRC32 != NULL) - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (length) { - long readSize; - - readSize = sizeof(tmpBuf); - if (readSize > length) - readSize = length; - - count = fread(tmpBuf, 1, readSize, srcFp); - if ((long) count != readSize) { // error or unexpected EOF - LOGD("fread %d bytes failed\n", (int) readSize); - return UNKNOWN_ERROR; - } - - if (pCRC32 != NULL) - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - - length -= readSize; - } - - return NO_ERROR; -} - -/* - * Compress all of the data in "srcFp" and write it to "dstFp". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the compressed data. - */ -status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - status_t result = NO_ERROR; - const size_t kBufSize = 32768; - unsigned char* inBuf = NULL; - unsigned char* outBuf = NULL; - z_stream zstream; - bool atEof = false; // no feof() aviailable yet - unsigned long crc; - int zerr; - - /* - * Create an input buffer and an output buffer. - */ - inBuf = new unsigned char[kBufSize]; - outBuf = new unsigned char[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - result = NO_MEMORY; - goto bail; - } - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - result = UNKNOWN_ERROR; - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - crc = crc32(0L, Z_NULL, 0); - - /* - * Loop while we have data. - */ - do { - size_t getSize; - int flush; - - /* only read if the input buffer is empty */ - if (zstream.avail_in == 0 && !atEof) { - LOGV("+++ reading %d bytes\n", (int)kBufSize); - if (data) { - getSize = size > kBufSize ? kBufSize : size; - memcpy(inBuf, data, getSize); - data = ((const char*)data) + getSize; - size -= getSize; - } else { - getSize = fread(inBuf, 1, kBufSize, srcFp); - if (ferror(srcFp)) { - LOGD("deflate read failed (errno=%d)\n", errno); - goto z_bail; - } - } - if (getSize < kBufSize) { - LOGV("+++ got %d bytes, EOF reached\n", - (int)getSize); - atEof = true; - } - - crc = crc32(crc, inBuf, getSize); - - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } - - if (atEof) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib deflate call failed (zerr=%d)\n", zerr); - result = UNKNOWN_ERROR; - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) - { - LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); - if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != - (size_t)(zstream.next_out - outBuf)) - { - LOGD("write %d failed in deflate\n", - (int) (zstream.next_out - outBuf)); - goto z_bail; - } - - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pCRC32 = crc; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] inBuf; - delete[] outBuf; - - return result; -} - -/* - * Mark an entry as deleted. - * - * We will eventually need to crunch the file down, but if several files - * are being removed (perhaps as part of an "update" process) we can make - * things considerably faster by deferring the removal to "flush" time. - */ -status_t ZipFile::remove(ZipEntry* pEntry) -{ - /* - * Should verify that pEntry is actually part of this archive, and - * not some stray ZipEntry from a different file. - */ - - /* mark entry as deleted, and mark archive as dirty */ - pEntry->setDeleted(); - mNeedCDRewrite = true; - return NO_ERROR; -} - -/* - * Flush any pending writes. - * - * In particular, this will crunch out deleted entries, and write the - * Central Directory and EOCD if we have stomped on them. - */ -status_t ZipFile::flush(void) -{ - status_t result = NO_ERROR; - long eocdPosn; - int i, count; - - if (mReadOnly) - return INVALID_OPERATION; - if (!mNeedCDRewrite) - return NO_ERROR; - - assert(mZipFp != NULL); - - result = crunchArchive(); - if (result != NO_ERROR) - return result; - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - count = mEntries.size(); - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - pEntry->mCDE.write(mZipFp); - } - - eocdPosn = ftell(mZipFp); - mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; - - mEOCD.write(mZipFp); - - /* - * If we had some stuff bloat up during compression and get replaced - * with plain files, or if we deleted some entries, there's a lot - * of wasted space at the end of the file. Remove it now. - */ - if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { - LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); - // not fatal - } - - /* should we clear the "newly added" flag in all entries now? */ - - mNeedCDRewrite = false; - return NO_ERROR; -} - -/* - * Crunch deleted files out of an archive by shifting the later files down. - * - * Because we're not using a temp file, we do the operation inside the - * current file. - */ -status_t ZipFile::crunchArchive(void) -{ - status_t result = NO_ERROR; - int i, count; - long delCount, adjust; - -#if 0 - printf("CONTENTS:\n"); - for (i = 0; i < (int) mEntries.size(); i++) { - printf(" %d: lfhOff=%ld del=%d\n", - i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); - } - printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); -#endif - - /* - * Roll through the set of files, shifting them as appropriate. We - * could probably get a slight performance improvement by sliding - * multiple files down at once (because we could use larger reads - * when operating on batches of small files), but it's not that useful. - */ - count = mEntries.size(); - delCount = adjust = 0; - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - long span; - - if (pEntry->getLFHOffset() != 0) { - long nextOffset; - - /* Get the length of this entry by finding the offset - * of the next entry. Directory entries don't have - * file offsets, so we need to find the next non-directory - * entry. - */ - nextOffset = 0; - for (int ii = i+1; nextOffset == 0 && ii < count; ii++) - nextOffset = mEntries[ii]->getLFHOffset(); - if (nextOffset == 0) - nextOffset = mEOCD.mCentralDirOffset; - span = nextOffset - pEntry->getLFHOffset(); - - assert(span >= ZipEntry::LocalFileHeader::kLFHLen); - } else { - /* This is a directory entry. It doesn't have - * any actual file contents, so there's no need to - * move anything. - */ - span = 0; - } - - //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", - // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); - - if (pEntry->getDeleted()) { - adjust += span; - delCount++; - - delete pEntry; - mEntries.removeAt(i); - - /* adjust loop control */ - count--; - i--; - } else if (span != 0 && adjust > 0) { - /* shuffle this entry back */ - //printf("+++ Shuffling '%s' back %ld\n", - // pEntry->getFileName(), adjust); - result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, - pEntry->getLFHOffset(), span); - if (result != NO_ERROR) { - /* this is why you use a temp file */ - LOGE("error during crunch - archive is toast\n"); - return result; - } - - pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); - } - } - - /* - * Fix EOCD info. We have to wait until the end to do some of this - * because we use mCentralDirOffset to determine "span" for the - * last entry. - */ - mEOCD.mCentralDirOffset -= adjust; - mEOCD.mNumEntries -= delCount; - mEOCD.mTotalNumEntries -= delCount; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - - assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); - assert(mEOCD.mNumEntries == count); - - return result; -} - -/* - * Works like memmove(), but on pieces of a file. - */ -status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) -{ - if (dst == src || n <= 0) - return NO_ERROR; - - unsigned char readBuf[32768]; - - if (dst < src) { - /* shift stuff toward start of file; must read from start */ - while (n != 0) { - size_t getSize = sizeof(readBuf); - if (getSize > n) - getSize = n; - - if (fseek(fp, (long) src, SEEK_SET) != 0) { - LOGD("filemove src seek %ld failed\n", (long) src); - return UNKNOWN_ERROR; - } - - if (fread(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove read %ld off=%ld failed\n", - (long) getSize, (long) src); - return UNKNOWN_ERROR; - } - - if (fseek(fp, (long) dst, SEEK_SET) != 0) { - LOGD("filemove dst seek %ld failed\n", (long) dst); - return UNKNOWN_ERROR; - } - - if (fwrite(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove write %ld off=%ld failed\n", - (long) getSize, (long) dst); - return UNKNOWN_ERROR; - } - - src += getSize; - dst += getSize; - n -= getSize; - } - } else { - /* shift stuff toward end of file; must read from end */ - assert(false); // write this someday, maybe - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Get the modification time from a file descriptor. - */ -time_t ZipFile::getModTime(int fd) -{ - struct stat sb; - - if (fstat(fd, &sb) < 0) { - LOGD("HEY: fstat on fd %d failed\n", fd); - return (time_t) -1; - } - - return sb.st_mtime; -} - - -#if 0 /* this is a bad idea */ -/* - * Get a copy of the Zip file descriptor. - * - * We don't allow this if the file was opened read-write because we tend - * to leave the file contents in an uncertain state between calls to - * flush(). The duplicated file descriptor should only be valid for reads. - */ -int ZipFile::getZipFd(void) const -{ - if (!mReadOnly) - return INVALID_OPERATION; - assert(mZipFp != NULL); - - int fd; - fd = dup(fileno(mZipFp)); - if (fd < 0) { - LOGD("didn't work, errno=%d\n", errno); - } - - return fd; -} -#endif - - -#if 0 -/* - * Expand data. - */ -bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const -{ - return false; -} -#endif - -// free the memory when you're done -void* ZipFile::uncompress(const ZipEntry* entry) -{ - size_t unlen = entry->getUncompressedLen(); - size_t clen = entry->getCompressedLen(); - - void* buf = malloc(unlen); - if (buf == NULL) { - return NULL; - } - - fseek(mZipFp, 0, SEEK_SET); - - off_t offset = entry->getFileOffset(); - if (fseek(mZipFp, offset, SEEK_SET) != 0) { - goto bail; - } - - switch (entry->getCompressionMethod()) - { - case ZipEntry::kCompressStored: { - ssize_t amt = fread(buf, 1, unlen, mZipFp); - if (amt != (ssize_t)unlen) { - goto bail; - } -#if 0 - printf("data...\n"); - const unsigned char* p = (unsigned char*)buf; - const unsigned char* end = p+unlen; - for (int i=0; i<32 && p < end; i++) { - printf("0x%08x ", (int)(offset+(i*0x10))); - for (int j=0; j<0x10 && p < end; j++) { - printf(" %02x", *p); - p++; - } - printf("\n"); - } -#endif - - } - break; - case ZipEntry::kCompressDeflated: { - if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { - goto bail; - } - } - break; - default: - goto bail; - } - return buf; - -bail: - free(buf); - return NULL; -} - - -/* - * =========================================================================== - * ZipFile::EndOfCentralDir - * =========================================================================== - */ - -/* - * Read the end-of-central-dir fields. - * - * "buf" should be positioned at the EOCD signature, and should contain - * the entire EOCD area including the comment. - */ -status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) -{ - /* don't allow re-use */ - assert(mComment == NULL); - - if (len < kEOCDLen) { - /* looks like ZIP file got truncated */ - LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", - kEOCDLen, len); - return INVALID_OPERATION; - } - - /* this should probably be an assert() */ - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) - return UNKNOWN_ERROR; - - mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); - mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); - mNumEntries = ZipEntry::getShortLE(&buf[0x08]); - mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); - mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); - mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); - mCommentLen = ZipEntry::getShortLE(&buf[0x14]); - - // TODO: validate mCentralDirOffset - - if (mCommentLen > 0) { - if (kEOCDLen + mCommentLen > len) { - LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", - kEOCDLen, mCommentLen, len); - return UNKNOWN_ERROR; - } - mComment = new unsigned char[mCommentLen]; - memcpy(mComment, buf + kEOCDLen, mCommentLen); - } - - return NO_ERROR; -} - -/* - * Write an end-of-central-directory section. - */ -status_t ZipFile::EndOfCentralDir::write(FILE* fp) -{ - unsigned char buf[kEOCDLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mDiskNumber); - ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); - ZipEntry::putShortLE(&buf[0x08], mNumEntries); - ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); - ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); - ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); - ZipEntry::putShortLE(&buf[0x14], mCommentLen); - - if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) - return UNKNOWN_ERROR; - if (mCommentLen > 0) { - assert(mComment != NULL); - if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of an EndOfCentralDir object. - */ -void ZipFile::EndOfCentralDir::dump(void) const -{ - LOGD(" EndOfCentralDir contents:\n"); - LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", - mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); - LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", - mCentralDirSize, mCentralDirOffset, mCommentLen); -} - diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp deleted file mode 100644 index 2e3c3a0..0000000 --- a/libs/utils/executablepath_darwin.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 <utils/executablepath.h> -#import <Carbon/Carbon.h> -#include <unistd.h> - -void executablepath(char s[PATH_MAX]) -{ - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - CFDictionaryRef dict; - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, - CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); -} - diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp deleted file mode 100644 index b8d2a3d..0000000 --- a/libs/utils/executablepath_linux.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 <utils/executablepath.h> -#include <sys/types.h> -#include <unistd.h> -#include <limits.h> -#include <stdio.h> - -void executablepath(char exe[PATH_MAX]) -{ - char proc[100]; - sprintf(proc, "/proc/%d/exe", getpid()); - - int err = readlink(proc, exe, PATH_MAX); -} - diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c deleted file mode 100644 index ab48c69..0000000 --- a/libs/utils/futex_synchro.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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 <stdio.h> -#include <limits.h> - -#include <sys/time.h> -#include <sched.h> - -#include <errno.h> - -#include <private/utils/futex_synchro.h> - - -// This futex glue code is need on desktop linux, but is already part of bionic. -#if !defined(HAVE_FUTEX_WRAPPERS) - -#include <unistd.h> -#include <sys/syscall.h> -typedef unsigned int u32; -#define asmlinkage -#define __user -#include <linux/futex.h> -#include <utils/Atomic.h> - - -int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) -{ - int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); - return err == 0 ? 0 : -errno; -} - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) -{ - return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); -} - -int __futex_wake(volatile void *ftx, int count) -{ - return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); -} - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr) -{ - return android_atomic_cmpxchg(old, _new, ptr); -} - -int __atomic_swap(int _new, volatile int *ptr) -{ - return android_atomic_swap(_new, ptr); -} - -int __atomic_dec(volatile int *ptr) -{ - return android_atomic_dec(ptr); -} - -#else // !defined(__arm__) - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); -int __futex_wake(volatile void *ftx, int count); - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr); -int __atomic_swap(int _new, volatile int *ptr); -int __atomic_dec(volatile int *ptr); - -#endif // !defined(HAVE_FUTEX_WRAPPERS) - - -// lock states -// -// 0: unlocked -// 1: locked, no waiters -// 2: locked, maybe waiters - -void futex_mutex_init(futex_mutex_t *m) -{ - m->value = 0; -} - -int futex_mutex_lock(futex_mutex_t *m, unsigned msec) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - if(msec == FUTEX_WAIT_INFINITE) { - while(__atomic_swap(2, &m->value) != 0) { - __futex_wait(&m->value, 2, 0); - } - } else { - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - while(__atomic_swap(2, &m->value) != 0) { - if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { - return -1; - } - } - } - return 0; -} - -int futex_mutex_trylock(futex_mutex_t *m) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - return -1; -} - -void futex_mutex_unlock(futex_mutex_t *m) -{ - if(__atomic_dec(&m->value) != 1) { - m->value = 0; - __futex_wake(&m->value, 1); - } -} - -/* XXX *technically* there is a race condition that could allow - * XXX a signal to be missed. If thread A is preempted in _wait() - * XXX after unlocking the mutex and before waiting, and if other - * XXX threads call signal or broadcast UINT_MAX times (exactly), - * XXX before thread A is scheduled again and calls futex_wait(), - * XXX then the signal will be lost. - */ - -void futex_cond_init(futex_cond_t *c) -{ - c->value = 0; -} - -int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) -{ - if(msec == FUTEX_WAIT_INFINITE){ - int oldvalue = c->value; - futex_mutex_unlock(m); - __futex_wait(&c->value, oldvalue, 0); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return 0; - } else { - int oldvalue = c->value; - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - futex_mutex_unlock(m); - const int err = __futex_wait(&c->value, oldvalue, &ts); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return err; - } -} - -void futex_cond_signal(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, 1); -} - -void futex_cond_broadcast(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, INT_MAX); -} - diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp deleted file mode 100644 index 656e46f..0000000 --- a/libs/utils/ported.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Ports of standard functions that don't exist on a specific platform. -// -// Note these are NOT in the "android" namespace. -// -#include <utils/ported.h> - -#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) -# include <sys/time.h> -# include <windows.h> -#endif - - -#if defined(NEED_GETTIMEOFDAY) -/* - * Replacement gettimeofday() for Windows environments (primarily MinGW). - * - * Ignores "tz". - */ -int gettimeofday(struct timeval* ptv, struct timezone* tz) -{ - long long nsTime; // time in 100ns units since Jan 1 1601 - FILETIME ft; - - if (tz != NULL) { - // oh well - } - - ::GetSystemTimeAsFileTime(&ft); - nsTime = (long long) ft.dwHighDateTime << 32 | - (long long) ft.dwLowDateTime; - // convert to time in usec since Jan 1 1970 - ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); - ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); - - return 0; -} -#endif - -#if defined(NEED_USLEEP) -// -// Replacement usleep for Windows environments (primarily MinGW). -// -void usleep(unsigned long usec) -{ - // Win32 API function Sleep() takes milliseconds - ::Sleep((usec + 500) / 1000); -} -#endif - -#if 0 //defined(NEED_PIPE) -// -// Replacement pipe() command for MinGW -// -// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the -// SecurityAttributes argument to CreatePipe(). This means the handles -// aren't inherited when a new process is created. The examples I've seen -// use it, possibly because there's a lot of junk going on behind the -// scenes. (I'm assuming "process" and "thread" are different here, so -// we should be okay spinning up a thread.) The recommended practice is -// to dup() the descriptor you want the child to have. -// -// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. -// You can't use select() either, since that only works on sockets. The -// Windows API calls that are useful here all operate on a HANDLE, not -// an integer file descriptor, and I don't think you can get there from -// here. The "named pipe" stuff is insane. -// -int pipe(int filedes[2]) -{ - return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); -} -#endif - -#if defined(NEED_SETENV) -/* - * MinGW lacks these. For now, just stub them out so the code compiles. - */ -int setenv(const char* name, const char* value, int overwrite) -{ - return 0; -} -void unsetenv(const char* name) -{ -} -char* getenv(const char* name) -{ - return NULL; -} -#endif |