summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/A2dpAudioInterface.cpp466
-rw-r--r--services/audioflinger/A2dpAudioInterface.h135
-rw-r--r--services/audioflinger/Android.mk130
-rw-r--r--services/audioflinger/AudioBufferProvider.h49
-rw-r--r--services/audioflinger/AudioDumpInterface.cpp531
-rw-r--r--services/audioflinger/AudioDumpInterface.h166
-rw-r--r--services/audioflinger/AudioFlinger.cpp4055
-rw-r--r--services/audioflinger/AudioFlinger.h807
-rw-r--r--services/audioflinger/AudioHardwareGeneric.cpp411
-rw-r--r--services/audioflinger/AudioHardwareGeneric.h151
-rw-r--r--services/audioflinger/AudioHardwareInterface.cpp182
-rw-r--r--services/audioflinger/AudioHardwareStub.cpp209
-rw-r--r--services/audioflinger/AudioHardwareStub.h106
-rw-r--r--services/audioflinger/AudioMixer.cpp915
-rw-r--r--services/audioflinger/AudioMixer.h193
-rw-r--r--services/audioflinger/AudioPolicyManagerBase.cpp1972
-rw-r--r--services/audioflinger/AudioPolicyService.cpp924
-rw-r--r--services/audioflinger/AudioPolicyService.h223
-rw-r--r--services/audioflinger/AudioResampler.cpp595
-rw-r--r--services/audioflinger/AudioResampler.h93
-rw-r--r--services/audioflinger/AudioResamplerCubic.cpp184
-rw-r--r--services/audioflinger/AudioResamplerCubic.h68
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp358
-rw-r--r--services/audioflinger/AudioResamplerSinc.h88
-rw-r--r--services/camera/libcameraservice/Android.mk71
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.cpp402
-rw-r--r--services/camera/libcameraservice/CameraHardwareStub.h135
-rw-r--r--services/camera/libcameraservice/CameraService.cpp1417
-rw-r--r--services/camera/libcameraservice/CameraService.h227
-rw-r--r--services/camera/libcameraservice/CannedJpeg.h734
-rw-r--r--services/camera/libcameraservice/FakeCamera.cpp430
-rw-r--r--services/camera/libcameraservice/FakeCamera.h67
-rw-r--r--services/camera/tests/CameraServiceTest/Android.mk24
-rw-r--r--services/camera/tests/CameraServiceTest/CameraServiceTest.cpp849
-rw-r--r--services/java/com/android/server/WindowManagerService.java7
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java13
-rw-r--r--services/java/com/android/server/am/AppWaitingForDebuggerDialog.java3
-rw-r--r--services/java/com/android/server/am/FactoryErrorDialog.java4
-rw-r--r--services/surfaceflinger/Android.mk51
-rw-r--r--services/surfaceflinger/Barrier.h59
-rw-r--r--services/surfaceflinger/BlurFilter.cpp376
-rw-r--r--services/surfaceflinger/BlurFilter.h35
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp364
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h118
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp401
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h96
-rw-r--r--services/surfaceflinger/Layer.cpp630
-rw-r--r--services/surfaceflinger/Layer.h134
-rw-r--r--services/surfaceflinger/LayerBase.cpp829
-rw-r--r--services/surfaceflinger/LayerBase.h389
-rw-r--r--services/surfaceflinger/LayerBlur.cpp265
-rw-r--r--services/surfaceflinger/LayerBlur.h69
-rw-r--r--services/surfaceflinger/LayerBuffer.cpp727
-rw-r--r--services/surfaceflinger/LayerBuffer.h225
-rw-r--r--services/surfaceflinger/LayerDim.cpp168
-rw-r--r--services/surfaceflinger/LayerDim.h60
-rw-r--r--services/surfaceflinger/MODULE_LICENSE_APACHE20
-rw-r--r--services/surfaceflinger/MessageQueue.cpp192
-rw-r--r--services/surfaceflinger/MessageQueue.h127
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp1940
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h420
-rw-r--r--services/surfaceflinger/Tokenizer.cpp173
-rw-r--r--services/surfaceflinger/Tokenizer.h57
-rw-r--r--services/surfaceflinger/Transform.cpp392
-rw-r--r--services/surfaceflinger/Transform.h128
-rw-r--r--services/surfaceflinger/clz.cpp37
-rw-r--r--services/surfaceflinger/clz.h37
-rw-r--r--services/surfaceflinger/tests/Android.mk1
-rw-r--r--services/surfaceflinger/tests/overlays/Android.mk17
-rw-r--r--services/surfaceflinger/tests/overlays/overlays.cpp59
-rw-r--r--services/surfaceflinger/tests/resize/Android.mk17
-rw-r--r--services/surfaceflinger/tests/resize/resize.cpp62
72 files changed, 26042 insertions, 7 deletions
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
new file mode 100644
index 0000000..995e31c
--- /dev/null
+++ b/services/audioflinger/A2dpAudioInterface.cpp
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "A2dpAudioInterface"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "A2dpAudioInterface.h"
+#include "audio/liba2dp.h"
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
+//{
+// AudioHardwareInterface* hw = 0;
+//
+// hw = AudioHardwareInterface::create();
+// LOGD("new A2dpAudioInterface(hw: %p)", hw);
+// hw = new A2dpAudioInterface(hw);
+// return hw;
+//}
+
+A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
+ mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
+{
+}
+
+A2dpAudioInterface::~A2dpAudioInterface()
+{
+ closeOutputStream((AudioStreamOut *)mOutput);
+ delete mHardwareInterface;
+}
+
+status_t A2dpAudioInterface::initCheck()
+{
+ if (mHardwareInterface == 0) return NO_INIT;
+ return mHardwareInterface->initCheck();
+}
+
+AudioStreamOut* A2dpAudioInterface::openOutputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
+ LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
+ return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
+ }
+
+ status_t err = 0;
+
+ // only one output stream allowed
+ if (mOutput) {
+ if (status)
+ *status = -1;
+ return NULL;
+ }
+
+ // create new output stream
+ A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
+ if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
+ mOutput = out;
+ mOutput->setBluetoothEnabled(mBluetoothEnabled);
+ mOutput->setSuspended(mSuspended);
+ } else {
+ delete out;
+ }
+
+ if (status)
+ *status = err;
+ return mOutput;
+}
+
+void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
+ if (mOutput == 0 || mOutput != out) {
+ mHardwareInterface->closeOutputStream(out);
+ }
+ else {
+ delete mOutput;
+ mOutput = 0;
+ }
+}
+
+
+AudioStreamIn* A2dpAudioInterface::openInputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
+}
+
+void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
+{
+ return mHardwareInterface->closeInputStream(in);
+}
+
+status_t A2dpAudioInterface::setMode(int mode)
+{
+ return mHardwareInterface->setMode(mode);
+}
+
+status_t A2dpAudioInterface::setMicMute(bool state)
+{
+ return mHardwareInterface->setMicMute(state);
+}
+
+status_t A2dpAudioInterface::getMicMute(bool* state)
+{
+ return mHardwareInterface->getMicMute(state);
+}
+
+status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ String8 key;
+ status_t status = NO_ERROR;
+
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ key = "bluetooth_enabled";
+ if (param.get(key, value) == NO_ERROR) {
+ mBluetoothEnabled = (value == "true");
+ if (mOutput) {
+ mOutput->setBluetoothEnabled(mBluetoothEnabled);
+ }
+ param.remove(key);
+ }
+ key = String8("A2dpSuspended");
+ if (param.get(key, value) == NO_ERROR) {
+ mSuspended = (value == "true");
+ if (mOutput) {
+ mOutput->setSuspended(mSuspended);
+ }
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status_t hwStatus = mHardwareInterface->setParameters(param.toString());
+ if (status == NO_ERROR) {
+ status = hwStatus;
+ }
+ }
+
+ return status;
+}
+
+String8 A2dpAudioInterface::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ AudioParameter a2dpParam = AudioParameter();
+ String8 value;
+ String8 key;
+
+ key = "bluetooth_enabled";
+ if (param.get(key, value) == NO_ERROR) {
+ value = mBluetoothEnabled ? "true" : "false";
+ a2dpParam.add(key, value);
+ param.remove(key);
+ }
+ key = "A2dpSuspended";
+ if (param.get(key, value) == NO_ERROR) {
+ value = mSuspended ? "true" : "false";
+ a2dpParam.add(key, value);
+ param.remove(key);
+ }
+
+ String8 keyValuePairs = a2dpParam.toString();
+
+ if (param.size()) {
+ if (keyValuePairs != "") {
+ keyValuePairs += ";";
+ }
+ keyValuePairs += mHardwareInterface->getParameters(param.toString());
+ }
+
+ LOGV("getParameters() %s", keyValuePairs.string());
+ return keyValuePairs;
+}
+
+size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+ return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
+}
+
+status_t A2dpAudioInterface::setVoiceVolume(float v)
+{
+ return mHardwareInterface->setVoiceVolume(v);
+}
+
+status_t A2dpAudioInterface::setMasterVolume(float v)
+{
+ return mHardwareInterface->setMasterVolume(v);
+}
+
+status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
+{
+ return mHardwareInterface->dumpState(fd, args);
+}
+
+// ----------------------------------------------------------------------------
+
+A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
+ mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
+ // assume BT enabled to start, this is safe because its only the
+ // enabled->disabled transition we are worried about
+ mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
+{
+ // use any address by default
+ strcpy(mA2dpAddress, "00:00:00:00:00:00");
+ init();
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
+ uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
+{
+ int lFormat = pFormat ? *pFormat : 0;
+ uint32_t lChannels = pChannels ? *pChannels : 0;
+ uint32_t lRate = pRate ? *pRate : 0;
+
+ LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
+
+ // fix up defaults
+ if (lFormat == 0) lFormat = format();
+ if (lChannels == 0) lChannels = channels();
+ if (lRate == 0) lRate = sampleRate();
+
+ // check values
+ if ((lFormat != format()) ||
+ (lChannels != channels()) ||
+ (lRate != sampleRate())){
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
+ return BAD_VALUE;
+ }
+
+ if (pFormat) *pFormat = lFormat;
+ if (pChannels) *pChannels = lChannels;
+ if (pRate) *pRate = lRate;
+
+ mDevice = device;
+ return NO_ERROR;
+}
+
+A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
+{
+ LOGV("A2dpAudioStreamOut destructor");
+ standby();
+ close();
+ LOGV("A2dpAudioStreamOut destructor returning from close()");
+}
+
+ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
+{
+ Mutex::Autolock lock(mLock);
+
+ size_t remaining = bytes;
+ status_t status = -1;
+
+ if (!mBluetoothEnabled || mClosing || mSuspended) {
+ LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
+ mBluetoothEnabled %d, mClosing %d, mSuspended %d",
+ mBluetoothEnabled, mClosing, mSuspended);
+ goto Error;
+ }
+
+ status = init();
+ if (status < 0)
+ goto Error;
+
+ while (remaining > 0) {
+ status = a2dp_write(mData, buffer, remaining);
+ if (status <= 0) {
+ LOGE("a2dp_write failed err: %d\n", status);
+ goto Error;
+ }
+ remaining -= status;
+ buffer = ((char *)buffer) + status;
+ }
+
+ mStandby = false;
+
+ return bytes;
+
+Error:
+ // Simulate audio output timing in case of error
+ usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
+
+ return status;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
+{
+ if (!mData) {
+ status_t status = a2dp_init(44100, 2, &mData);
+ if (status < 0) {
+ LOGE("a2dp_init failed err: %d\n", status);
+ mData = NULL;
+ return status;
+ }
+ a2dp_set_sink(mData, mA2dpAddress);
+ }
+
+ return 0;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
+{
+ int result = 0;
+
+ if (mClosing) {
+ LOGV("Ignore standby, closing");
+ return result;
+ }
+
+ Mutex::Autolock lock(mLock);
+
+ if (!mStandby) {
+ result = a2dp_stop(mData);
+ if (result == 0)
+ mStandby = true;
+ }
+
+ return result;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ String8 key = String8("a2dp_sink_address");
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
+
+ if (param.get(key, value) == NO_ERROR) {
+ if (value.length() != strlen("00:00:00:00:00:00")) {
+ status = BAD_VALUE;
+ } else {
+ setAddress(value.string());
+ }
+ param.remove(key);
+ }
+ key = String8("closing");
+ if (param.get(key, value) == NO_ERROR) {
+ mClosing = (value == "true");
+ param.remove(key);
+ }
+ key = AudioParameter::keyRouting;
+ if (param.getInt(key, device) == NO_ERROR) {
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) {
+ mDevice = device;
+ status = NO_ERROR;
+ } else {
+ status = BAD_VALUE;
+ }
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8("a2dp_sink_address");
+
+ if (param.get(key, value) == NO_ERROR) {
+ value = mA2dpAddress;
+ param.add(key, value);
+ }
+ key = AudioParameter::keyRouting;
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
+{
+ Mutex::Autolock lock(mLock);
+
+ if (strlen(address) != strlen("00:00:00:00:00:00"))
+ return -EINVAL;
+
+ strcpy(mA2dpAddress, address);
+ if (mData)
+ a2dp_set_sink(mData, mA2dpAddress);
+
+ return NO_ERROR;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
+{
+ LOGD("setBluetoothEnabled %d", enabled);
+
+ Mutex::Autolock lock(mLock);
+
+ mBluetoothEnabled = enabled;
+ if (!enabled) {
+ return close_l();
+ }
+ return NO_ERROR;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
+{
+ LOGV("setSuspended %d", onOff);
+ mSuspended = onOff;
+ standby();
+ return NO_ERROR;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
+{
+ Mutex::Autolock lock(mLock);
+ LOGV("A2dpAudioStreamOut::close() calling close_l()");
+ return close_l();
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
+{
+ if (mData) {
+ LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
+ a2dp_cleanup(mData);
+ mData = NULL;
+ }
+ return NO_ERROR;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
+{
+ //TODO: enable when supported by driver
+ return INVALID_OPERATION;
+}
+
+}; // namespace android
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
new file mode 100644
index 0000000..48154f9
--- /dev/null
+++ b/services/audioflinger/A2dpAudioInterface.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A2DP_AUDIO_HARDWARE_H
+#define A2DP_AUDIO_HARDWARE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+
+
+namespace android {
+
+class A2dpAudioInterface : public AudioHardwareBase
+{
+ class A2dpAudioStreamOut;
+
+public:
+ A2dpAudioInterface(AudioHardwareInterface* hw);
+ virtual ~A2dpAudioInterface();
+ virtual status_t initCheck();
+
+ virtual status_t setVoiceVolume(float volume);
+ virtual status_t setMasterVolume(float volume);
+
+ virtual status_t setMode(int mode);
+
+ // mic mute
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool* state);
+
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
+
+ // create I/O streams
+ virtual AudioStreamOut* openOutputStream(
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
+ status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
+ virtual AudioStreamIn* openInputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+// static AudioHardwareInterface* createA2dpInterface();
+
+protected:
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ class A2dpAudioStreamOut : public AudioStreamOut {
+ public:
+ A2dpAudioStreamOut();
+ virtual ~A2dpAudioStreamOut();
+ status_t set(uint32_t device,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate);
+ virtual uint32_t sampleRate() const { return 44100; }
+ // SBC codec wants a multiple of 512
+ virtual size_t bufferSize() const { return 512 * 20; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
+ virtual int format() const { return AudioSystem::PCM_16_BIT; }
+ virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; }
+ virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ status_t standby();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+ private:
+ friend class A2dpAudioInterface;
+ status_t init();
+ status_t close();
+ status_t close_l();
+ status_t setAddress(const char* address);
+ status_t setBluetoothEnabled(bool enabled);
+ status_t setSuspended(bool onOff);
+
+ private:
+ int mFd;
+ bool mStandby;
+ int mStartCount;
+ int mRetryCount;
+ char mA2dpAddress[20];
+ void* mData;
+ Mutex mLock;
+ bool mBluetoothEnabled;
+ uint32_t mDevice;
+ bool mClosing;
+ bool mSuspended;
+ };
+
+ friend class A2dpAudioStreamOut;
+
+ A2dpAudioStreamOut* mOutput;
+ AudioHardwareInterface *mHardwareInterface;
+ char mA2dpAddress[20];
+ bool mBluetoothEnabled;
+ bool mSuspended;
+};
+
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // A2DP_AUDIO_HARDWARE_H
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
new file mode 100644
index 0000000..870c0b8
--- /dev/null
+++ b/services/audioflinger/Android.mk
@@ -0,0 +1,130 @@
+LOCAL_PATH:= $(call my-dir)
+
+#AUDIO_POLICY_TEST := true
+#ENABLE_AUDIO_DUMP := true
+
+include $(CLEAR_VARS)
+
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ ENABLE_AUDIO_DUMP := true
+endif
+
+
+LOCAL_SRC_FILES:= \
+ AudioHardwareGeneric.cpp \
+ AudioHardwareStub.cpp \
+ AudioHardwareInterface.cpp
+
+ifeq ($(ENABLE_AUDIO_DUMP),true)
+ LOCAL_SRC_FILES += AudioDumpInterface.cpp
+ LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libmedia \
+ libhardware_legacy
+
+ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
+ LOCAL_CFLAGS += -DGENERIC_AUDIO
+endif
+
+LOCAL_MODULE:= libaudiointerface
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_SRC_FILES += A2dpAudioInterface.cpp
+ LOCAL_SHARED_LIBRARIES += liba2dp
+ LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
+ LOCAL_C_INCLUDES += $(call include-path-for, bluez)
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioPolicyManagerBase.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libmedia
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_MODULE:= libaudiopolicybase
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_CFLAGS += -DWITH_A2DP
+endif
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioFlinger.cpp \
+ AudioMixer.cpp.arm \
+ AudioResampler.cpp.arm \
+ AudioResamplerSinc.cpp.arm \
+ AudioResamplerCubic.cpp.arm \
+ AudioPolicyService.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libmedia \
+ libhardware_legacy
+
+ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
+ LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
+ LOCAL_CFLAGS += -DGENERIC_AUDIO
+else
+ LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
+endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_MODULE:= libaudioflinger
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
+ LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
+endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
+endif
+
+ifeq ($(BOARD_USE_LVMX),true)
+ LOCAL_CFLAGS += -DLVMX
+ LOCAL_C_INCLUDES += vendor/nxp
+ LOCAL_STATIC_LIBRARIES += liblifevibes
+ LOCAL_SHARED_LIBRARIES += liblvmxservice
+# LOCAL_SHARED_LIBRARIES += liblvmxipc
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h
new file mode 100644
index 0000000..81c5c39
--- /dev/null
+++ b/services/audioflinger/AudioBufferProvider.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_H
+#define ANDROID_AUDIO_BUFFER_PROVIDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class AudioBufferProvider
+{
+public:
+
+ struct Buffer {
+ union {
+ void* raw;
+ short* i16;
+ int8_t* i8;
+ };
+ size_t frameCount;
+ };
+
+ virtual ~AudioBufferProvider() {}
+
+ virtual status_t getNextBuffer(Buffer* buffer) = 0;
+ virtual void releaseBuffer(Buffer* buffer) = 0;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H
diff --git a/services/audioflinger/AudioDumpInterface.cpp b/services/audioflinger/AudioDumpInterface.cpp
new file mode 100644
index 0000000..a018b4c
--- /dev/null
+++ b/services/audioflinger/AudioDumpInterface.cpp
@@ -0,0 +1,531 @@
+/* //device/servers/AudioFlinger/AudioDumpInterface.cpp
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioFlingerDump"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "AudioDumpInterface.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
+ : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8(""))
+{
+ if(hw == 0) {
+ LOGE("Dump construct hw = 0");
+ }
+ mFinalInterface = hw;
+ LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface);
+}
+
+
+AudioDumpInterface::~AudioDumpInterface()
+{
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ closeOutputStream((AudioStreamOut *)mOutputs[i]);
+ }
+ if(mFinalInterface) delete mFinalInterface;
+}
+
+
+AudioStreamOut* AudioDumpInterface::openOutputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+ AudioStreamOut* outFinal = NULL;
+ int lFormat = AudioSystem::PCM_16_BIT;
+ uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ uint32_t lRate = 44100;
+
+
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) {
+ outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
+ if (outFinal != 0) {
+ lFormat = outFinal->format();
+ lChannels = outFinal->channels();
+ lRate = outFinal->sampleRate();
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
+ mFirstHwOutput = false;
+ }
+ }
+ } else {
+ if (format != 0 && *format != 0) {
+ lFormat = *format;
+ } else {
+ lFormat = AudioSystem::PCM_16_BIT;
+ }
+ if (channels != 0 && *channels != 0) {
+ lChannels = *channels;
+ } else {
+ lChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ }
+ if (sampleRate != 0 && *sampleRate != 0) {
+ lRate = *sampleRate;
+ } else {
+ lRate = 44100;
+ }
+ if (status) *status = NO_ERROR;
+ }
+ LOGV("openOutputStream(), outFinal %p", outFinal);
+
+ AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal,
+ devices, lFormat, lChannels, lRate);
+ mOutputs.add(dumOutput);
+
+ return dumOutput;
+}
+
+void AudioDumpInterface::closeOutputStream(AudioStreamOut* out)
+{
+ AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out;
+
+ if (mOutputs.indexOf(dumpOut) < 0) {
+ LOGW("Attempt to close invalid output stream");
+ return;
+ }
+
+ LOGV("closeOutputStream() output %p", out);
+
+ dumpOut->standby();
+ if (dumpOut->finalStream() != NULL) {
+ mFinalInterface->closeOutputStream(dumpOut->finalStream());
+ mFirstHwOutput = true;
+ }
+
+ mOutputs.remove(dumpOut);
+ delete dumpOut;
+}
+
+AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels,
+ uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
+{
+ AudioStreamIn* inFinal = NULL;
+ int lFormat = AudioSystem::PCM_16_BIT;
+ uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO;
+ uint32_t lRate = 8000;
+
+
+ if (mInputs.size() == 0) {
+ inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
+ if (inFinal == 0) return 0;
+
+ lFormat = inFinal->format();
+ lChannels = inFinal->channels();
+ lRate = inFinal->sampleRate();
+ } else {
+ if (format != 0 && *format != 0) lFormat = *format;
+ if (channels != 0 && *channels != 0) lChannels = *channels;
+ if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate;
+ if (status) *status = NO_ERROR;
+ }
+ LOGV("openInputStream(), inFinal %p", inFinal);
+
+ AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal,
+ devices, lFormat, lChannels, lRate);
+ mInputs.add(dumInput);
+
+ return dumInput;
+}
+void AudioDumpInterface::closeInputStream(AudioStreamIn* in)
+{
+ AudioStreamInDump *dumpIn = (AudioStreamInDump *)in;
+
+ if (mInputs.indexOf(dumpIn) < 0) {
+ LOGW("Attempt to close invalid input stream");
+ return;
+ }
+ dumpIn->standby();
+ if (dumpIn->finalStream() != NULL) {
+ mFinalInterface->closeInputStream(dumpIn->finalStream());
+ }
+
+ mInputs.remove(dumpIn);
+ delete dumpIn;
+}
+
+
+status_t AudioDumpInterface::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ int valueInt;
+ LOGV("setParameters %s", keyValuePairs.string());
+
+ if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
+ mFileName = value;
+ param.remove(String8("test_cmd_file_name"));
+ }
+ if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
+ Mutex::Autolock _l(mLock);
+ param.remove(String8("test_cmd_policy"));
+ mPolicyCommands = param.toString();
+ LOGV("test_cmd_policy command %s written", mPolicyCommands.string());
+ return NO_ERROR;
+ }
+
+ if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs);
+ return NO_ERROR;
+}
+
+String8 AudioDumpInterface::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ AudioParameter response;
+ String8 value;
+
+// LOGV("getParameters %s", keys.string());
+ if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
+ Mutex::Autolock _l(mLock);
+ if (mPolicyCommands.length() != 0) {
+ response = AudioParameter(mPolicyCommands);
+ response.addInt(String8("test_cmd_policy"), 1);
+ } else {
+ response.addInt(String8("test_cmd_policy"), 0);
+ }
+ param.remove(String8("test_cmd_policy"));
+// LOGV("test_cmd_policy command %s read", mPolicyCommands.string());
+ }
+
+ if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
+ response.add(String8("test_cmd_file_name"), mFileName);
+ param.remove(String8("test_cmd_file_name"));
+ }
+
+ String8 keyValuePairs = response.toString();
+
+ if (param.size() && mFinalInterface != 0 ) {
+ keyValuePairs += ";";
+ keyValuePairs += mFinalInterface->getParameters(param.toString());
+ }
+
+ return keyValuePairs;
+}
+
+
+// ----------------------------------------------------------------------------
+
+AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamOut* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate)
+ : mInterface(interface), mId(id),
+ mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices),
+ mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0)
+{
+ LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
+}
+
+
+AudioStreamOutDump::~AudioStreamOutDump()
+{
+ LOGV("AudioStreamOutDump destructor");
+ Close();
+}
+
+ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
+{
+ ssize_t ret;
+
+ if (mFinalStream) {
+ ret = mFinalStream->write(buffer, bytes);
+ } else {
+ usleep((bytes * 1000000) / frameSize() / sampleRate());
+ ret = bytes;
+ }
+ if(!mOutFile) {
+ if (mInterface->fileName() != "") {
+ char name[255];
+ sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
+ mOutFile = fopen(name, "wb");
+ LOGV("Opening dump file %s, fh %p", name, mOutFile);
+ }
+ }
+ if (mOutFile) {
+ fwrite(buffer, bytes, 1, mOutFile);
+ }
+ return ret;
+}
+
+status_t AudioStreamOutDump::standby()
+{
+ LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream);
+
+ Close();
+ if (mFinalStream != 0 ) return mFinalStream->standby();
+ return NO_ERROR;
+}
+
+uint32_t AudioStreamOutDump::sampleRate() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->sampleRate();
+ return mSampleRate;
+}
+
+size_t AudioStreamOutDump::bufferSize() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->bufferSize();
+ return mBufferSize;
+}
+
+uint32_t AudioStreamOutDump::channels() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->channels();
+ return mChannels;
+}
+int AudioStreamOutDump::format() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->format();
+ return mFormat;
+}
+uint32_t AudioStreamOutDump::latency() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->latency();
+ return 0;
+}
+status_t AudioStreamOutDump::setVolume(float left, float right)
+{
+ if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right);
+ return NO_ERROR;
+}
+status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
+{
+ LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string());
+
+ if (mFinalStream != 0 ) {
+ return mFinalStream->setParameters(keyValuePairs);
+ }
+
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ int valueInt;
+ status_t status = NO_ERROR;
+
+ if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) {
+ mId = valueInt;
+ }
+
+ if (param.getInt(String8("format"), valueInt) == NO_ERROR) {
+ if (mOutFile == 0) {
+ mFormat = valueInt;
+ } else {
+ status = INVALID_OPERATION;
+ }
+ }
+ if (param.getInt(String8("channels"), valueInt) == NO_ERROR) {
+ if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) {
+ mChannels = valueInt;
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) {
+ if (valueInt > 0 && valueInt <= 48000) {
+ if (mOutFile == 0) {
+ mSampleRate = valueInt;
+ } else {
+ status = INVALID_OPERATION;
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ return status;
+}
+
+String8 AudioStreamOutDump::getParameters(const String8& keys)
+{
+ if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
+
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args)
+{
+ if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
+ return NO_ERROR;
+}
+
+void AudioStreamOutDump::Close()
+{
+ if(mOutFile) {
+ fclose(mOutFile);
+ mOutFile = 0;
+ }
+}
+
+status_t AudioStreamOutDump::getRenderPosition(uint32_t *dspFrames)
+{
+ if (mFinalStream != 0 ) return mFinalStream->getRenderPosition(dspFrames);
+ return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamIn* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate)
+ : mInterface(interface), mId(id),
+ mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices),
+ mBufferSize(1024), mFinalStream(finalStream), mInFile(0)
+{
+ LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
+}
+
+
+AudioStreamInDump::~AudioStreamInDump()
+{
+ Close();
+}
+
+ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
+{
+ if (mFinalStream) {
+ return mFinalStream->read(buffer, bytes);
+ }
+
+ usleep((bytes * 1000000) / frameSize() / sampleRate());
+
+ if(!mInFile) {
+ char name[255];
+ strcpy(name, "/sdcard/music/sine440");
+ if (channels() == AudioSystem::CHANNEL_IN_MONO) {
+ strcat(name, "_mo");
+ } else {
+ strcat(name, "_st");
+ }
+ if (format() == AudioSystem::PCM_16_BIT) {
+ strcat(name, "_16b");
+ } else {
+ strcat(name, "_8b");
+ }
+ if (sampleRate() < 16000) {
+ strcat(name, "_8k");
+ } else if (sampleRate() < 32000) {
+ strcat(name, "_22k");
+ } else if (sampleRate() < 48000) {
+ strcat(name, "_44k");
+ } else {
+ strcat(name, "_48k");
+ }
+ strcat(name, ".wav");
+ mInFile = fopen(name, "rb");
+ LOGV("Opening dump file %s, fh %p", name, mInFile);
+ if (mInFile) {
+ fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ }
+
+ }
+ if (mInFile) {
+ ssize_t bytesRead = fread(buffer, bytes, 1, mInFile);
+ if (bytesRead != bytes) {
+ fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile);
+ }
+ }
+ return bytes;
+}
+
+status_t AudioStreamInDump::standby()
+{
+ LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream);
+
+ Close();
+ if (mFinalStream != 0 ) return mFinalStream->standby();
+ return NO_ERROR;
+}
+
+status_t AudioStreamInDump::setGain(float gain)
+{
+ if (mFinalStream != 0 ) return mFinalStream->setGain(gain);
+ return NO_ERROR;
+}
+
+uint32_t AudioStreamInDump::sampleRate() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->sampleRate();
+ return mSampleRate;
+}
+
+size_t AudioStreamInDump::bufferSize() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->bufferSize();
+ return mBufferSize;
+}
+
+uint32_t AudioStreamInDump::channels() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->channels();
+ return mChannels;
+}
+
+int AudioStreamInDump::format() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->format();
+ return mFormat;
+}
+
+status_t AudioStreamInDump::setParameters(const String8& keyValuePairs)
+{
+ LOGV("AudioStreamInDump::setParameters()");
+ if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs);
+ return NO_ERROR;
+}
+
+String8 AudioStreamInDump::getParameters(const String8& keys)
+{
+ if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
+
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+unsigned int AudioStreamInDump::getInputFramesLost() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->getInputFramesLost();
+ return 0;
+}
+
+status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args)
+{
+ if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
+ return NO_ERROR;
+}
+
+void AudioStreamInDump::Close()
+{
+ if(mInFile) {
+ fclose(mInFile);
+ mInFile = 0;
+ }
+}
+}; // namespace android
diff --git a/services/audioflinger/AudioDumpInterface.h b/services/audioflinger/AudioDumpInterface.h
new file mode 100644
index 0000000..4c62b3e
--- /dev/null
+++ b/services/audioflinger/AudioDumpInterface.h
@@ -0,0 +1,166 @@
+/* //device/servers/AudioFlinger/AudioDumpInterface.h
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_DUMP_INTERFACE_H
+#define ANDROID_AUDIO_DUMP_INTERFACE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+
+namespace android {
+
+#define AUDIO_DUMP_WAVE_HDR_SIZE 44
+
+class AudioDumpInterface;
+
+class AudioStreamOutDump : public AudioStreamOut {
+public:
+ AudioStreamOutDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamOut* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate);
+ ~AudioStreamOutDump();
+
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ virtual uint32_t sampleRate() const;
+ virtual size_t bufferSize() const;
+ virtual uint32_t channels() const;
+ virtual int format() const;
+ virtual uint32_t latency() const;
+ virtual status_t setVolume(float left, float right);
+ virtual status_t standby();
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ void Close(void);
+ AudioStreamOut* finalStream() { return mFinalStream; }
+ uint32_t device() { return mDevice; }
+ int getId() { return mId; }
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+private:
+ AudioDumpInterface *mInterface;
+ int mId;
+ uint32_t mSampleRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mLatency; //
+ uint32_t mDevice; // current device this output is routed to
+ size_t mBufferSize;
+ AudioStreamOut *mFinalStream;
+ FILE *mOutFile; // output file
+ int mFileCount;
+};
+
+class AudioStreamInDump : public AudioStreamIn {
+public:
+ AudioStreamInDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamIn* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate);
+ ~AudioStreamInDump();
+
+ virtual uint32_t sampleRate() const;
+ virtual size_t bufferSize() const;
+ virtual uint32_t channels() const;
+ virtual int format() const;
+
+ virtual status_t setGain(float gain);
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t standby();
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual unsigned int getInputFramesLost() const;
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ void Close(void);
+ AudioStreamIn* finalStream() { return mFinalStream; }
+ uint32_t device() { return mDevice; }
+
+private:
+ AudioDumpInterface *mInterface;
+ int mId;
+ uint32_t mSampleRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mDevice; // current device this output is routed to
+ size_t mBufferSize;
+ AudioStreamIn *mFinalStream;
+ FILE *mInFile; // output file
+};
+
+class AudioDumpInterface : public AudioHardwareBase
+{
+
+public:
+ AudioDumpInterface(AudioHardwareInterface* hw);
+ virtual AudioStreamOut* openOutputStream(
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
+ status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
+ virtual ~AudioDumpInterface();
+
+ virtual status_t initCheck()
+ {return mFinalInterface->initCheck();}
+ virtual status_t setVoiceVolume(float volume)
+ {return mFinalInterface->setVoiceVolume(volume);}
+ virtual status_t setMasterVolume(float volume)
+ {return mFinalInterface->setMasterVolume(volume);}
+
+ // mic mute
+ virtual status_t setMicMute(bool state)
+ {return mFinalInterface->setMicMute(state);}
+ virtual status_t getMicMute(bool* state)
+ {return mFinalInterface->getMicMute(state);}
+
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels,
+ uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+
+ virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
+
+ String8 fileName() const { return mFileName; }
+protected:
+
+ AudioHardwareInterface *mFinalInterface;
+ SortedVector<AudioStreamOutDump *> mOutputs;
+ bool mFirstHwOutput;
+ SortedVector<AudioStreamInDump *> mInputs;
+ Mutex mLock;
+ String8 mPolicyCommands;
+ String8 mFileName;
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_DUMP_INTERFACE_H
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
new file mode 100644
index 0000000..2414e8d
--- /dev/null
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -0,0 +1,4055 @@
+/* //device/include/server/AudioFlinger/AudioFlinger.cpp
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+
+#include <math.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+
+#include <cutils/properties.h>
+
+#include <media/AudioTrack.h>
+#include <media/AudioRecord.h>
+
+#include <private/media/AudioTrackShared.h>
+
+#include <hardware_legacy/AudioHardwareInterface.h>
+
+#include "AudioMixer.h"
+#include "AudioFlinger.h"
+
+#ifdef WITH_A2DP
+#include "A2dpAudioInterface.h"
+#endif
+
+#ifdef LVMX
+#include "lifevibes.h"
+#endif
+
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
+static const char* kHardwareLockedString = "Hardware lock is taken\n";
+
+//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
+static const float MAX_GAIN = 4096.0f;
+
+// retry counts for buffer fill timeout
+// 50 * ~20msecs = 1 second
+static const int8_t kMaxTrackRetries = 50;
+static const int8_t kMaxTrackStartupRetries = 50;
+// allow less retry attempts on direct output thread.
+// direct outputs can be a scarce resource in audio hardware and should
+// be released as quickly as possible.
+static const int8_t kMaxTrackRetriesDirect = 2;
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
+static const nsecs_t kWarningThrottle = seconds(5);
+
+
+#define AUDIOFLINGER_SECURITY_ENABLED 1
+
+// ----------------------------------------------------------------------------
+
+static bool recordingAllowed() {
+#ifndef HAVE_ANDROID_OS
+ return true;
+#endif
+#if AUDIOFLINGER_SECURITY_ENABLED
+ if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+ bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO"));
+ if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO");
+ return ok;
+#else
+ if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO")))
+ LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest");
+ return true;
+#endif
+}
+
+static bool settingsAllowed() {
+#ifndef HAVE_ANDROID_OS
+ return true;
+#endif
+#if AUDIOFLINGER_SECURITY_ENABLED
+ if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+ bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
+ if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
+ return ok;
+#else
+ if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")))
+ LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest");
+ return true;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::AudioFlinger()
+ : BnAudioFlinger(),
+ mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
+{
+ mHardwareStatus = AUDIO_HW_IDLE;
+
+ mAudioHardware = AudioHardwareInterface::create();
+
+ mHardwareStatus = AUDIO_HW_INIT;
+ if (mAudioHardware->initCheck() == NO_ERROR) {
+ // open 16-bit output stream for s/w mixer
+
+ setMode(AudioSystem::MODE_NORMAL);
+
+ setMasterVolume(1.0f);
+ setMasterMute(false);
+ } else {
+ LOGE("Couldn't even initialize the stubbed audio hardware!");
+ }
+#ifdef LVMX
+ LifeVibes::init();
+#endif
+}
+
+AudioFlinger::~AudioFlinger()
+{
+ while (!mRecordThreads.isEmpty()) {
+ // closeInput() will remove first entry from mRecordThreads
+ closeInput(mRecordThreads.keyAt(0));
+ }
+ while (!mPlaybackThreads.isEmpty()) {
+ // closeOutput() will remove first entry from mPlaybackThreads
+ closeOutput(mPlaybackThreads.keyAt(0));
+ }
+ if (mAudioHardware) {
+ delete mAudioHardware;
+ }
+}
+
+
+
+status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ result.append("Clients:\n");
+ for (size_t i = 0; i < mClients.size(); ++i) {
+ wp<Client> wClient = mClients.valueAt(i);
+ if (wClient != 0) {
+ sp<Client> client = wClient.promote();
+ if (client != 0) {
+ snprintf(buffer, SIZE, " pid: %d\n", client->pid());
+ result.append(buffer);
+ }
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+
+status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ int hardwareStatus = mHardwareStatus;
+
+ snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump AudioFlinger from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
+{
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ dumpPermissionDenial(fd, args);
+ } else {
+ // get state of hardware lock
+ bool hardwareLocked = tryLock(mHardwareLock);
+ if (!hardwareLocked) {
+ String8 result(kHardwareLockedString);
+ write(fd, result.string(), result.size());
+ } else {
+ mHardwareLock.unlock();
+ }
+
+ bool locked = tryLock(mLock);
+
+ // failed to lock - AudioFlinger is probably deadlocked
+ if (!locked) {
+ String8 result(kDeadlockedString);
+ write(fd, result.string(), result.size());
+ }
+
+ dumpClients(fd, args);
+ dumpInternals(fd, args);
+
+ // dump playback threads
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->dump(fd, args);
+ }
+
+ // dump record threads
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->dump(fd, args);
+ }
+
+ if (mAudioHardware) {
+ mAudioHardware->dumpState(fd, args);
+ }
+ if (locked) mLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+
+// IAudioFlinger interface
+
+
+sp<IAudioTrack> AudioFlinger::createTrack(
+ pid_t pid,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ int output,
+ status_t *status)
+{
+ sp<PlaybackThread::Track> track;
+ sp<TrackHandle> trackHandle;
+ sp<Client> client;
+ wp<Client> wclient;
+ status_t lStatus;
+
+ if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
+ LOGE("invalid stream type");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ {
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGE("unknown output thread");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ wclient = mClients.valueFor(pid);
+
+ if (wclient != NULL) {
+ client = wclient.promote();
+ } else {
+ client = new Client(this, pid);
+ mClients.add(pid, client);
+ }
+ track = thread->createTrack_l(client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer, &lStatus);
+ }
+ if (lStatus == NO_ERROR) {
+ trackHandle = new TrackHandle(track);
+ } else {
+ // remove local strong reference to Client before deleting the Track so that the Client
+ // destructor is called by the TrackBase destructor with mLock held
+ client.clear();
+ track.clear();
+ }
+
+Exit:
+ if(status) {
+ *status = lStatus;
+ }
+ return trackHandle;
+}
+
+uint32_t AudioFlinger::sampleRate(int output) const
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("sampleRate() unknown thread %d", output);
+ return 0;
+ }
+ return thread->sampleRate();
+}
+
+int AudioFlinger::channelCount(int output) const
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("channelCount() unknown thread %d", output);
+ return 0;
+ }
+ return thread->channelCount();
+}
+
+int AudioFlinger::format(int output) const
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("format() unknown thread %d", output);
+ return 0;
+ }
+ return thread->format();
+}
+
+size_t AudioFlinger::frameCount(int output) const
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("frameCount() unknown thread %d", output);
+ return 0;
+ }
+ return thread->frameCount();
+}
+
+uint32_t AudioFlinger::latency(int output) const
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("latency() unknown thread %d", output);
+ return 0;
+ }
+ return thread->latency();
+}
+
+status_t AudioFlinger::setMasterVolume(float value)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ // when hw supports master volume, don't scale in sw mixer
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+ if (mAudioHardware->setMasterVolume(value) == NO_ERROR) {
+ value = 1.0f;
+ }
+ mHardwareStatus = AUDIO_HW_IDLE;
+
+ mMasterVolume = value;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::setMode(int mode)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+ if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) {
+ LOGW("Illegal value: setMode(%d)", mode);
+ return BAD_VALUE;
+ }
+
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_HW_SET_MODE;
+ status_t ret = mAudioHardware->setMode(mode);
+#ifdef LVMX
+ if (NO_ERROR == ret) {
+ LifeVibes::setMode(mode);
+ }
+#endif
+ mHardwareStatus = AUDIO_HW_IDLE;
+ return ret;
+}
+
+status_t AudioFlinger::setMicMute(bool state)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
+ status_t ret = mAudioHardware->setMicMute(state);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ return ret;
+}
+
+bool AudioFlinger::getMicMute() const
+{
+ bool state = AudioSystem::MODE_INVALID;
+ mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
+ mAudioHardware->getMicMute(&state);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ return state;
+}
+
+status_t AudioFlinger::setMasterMute(bool muted)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ mMasterMute = muted;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMasterMute(muted);
+
+ return NO_ERROR;
+}
+
+float AudioFlinger::masterVolume() const
+{
+ return mMasterVolume;
+}
+
+bool AudioFlinger::masterMute() const
+{
+ return mMasterMute;
+}
+
+status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = NULL;
+ if (output) {
+ thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+ }
+
+ mStreamTypes[stream].volume = value;
+
+ if (thread == NULL) {
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
+ }
+ } else {
+ thread->setStreamVolume(stream, value);
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::setStreamMute(int stream, bool muted)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
+ uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
+ return BAD_VALUE;
+ }
+
+ mStreamTypes[stream].mute = muted;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setStreamMute(stream, muted);
+
+ return NO_ERROR;
+}
+
+float AudioFlinger::streamVolume(int stream, int output) const
+{
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ return 0.0f;
+ }
+
+ AutoMutex lock(mLock);
+ float volume;
+ if (output) {
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return 0.0f;
+ }
+ volume = thread->streamVolume(stream);
+ } else {
+ volume = mStreamTypes[stream].volume;
+ }
+
+ return volume;
+}
+
+bool AudioFlinger::streamMute(int stream) const
+{
+ if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) {
+ return true;
+ }
+
+ return mStreamTypes[stream].mute;
+}
+
+bool AudioFlinger::isStreamActive(int stream) const
+{
+ Mutex::Autolock _l(mLock);
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->isStreamActive(stream)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
+{
+ status_t result;
+
+ LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d",
+ ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+#ifdef LVMX
+ AudioParameter param = AudioParameter(keyValuePairs);
+ LifeVibes::setParameters(ioHandle,keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ int device;
+ if (NO_ERROR != param.getInt(key, device)) {
+ device = -1;
+ }
+
+ key = String8(LifevibesTag);
+ String8 value;
+ int musicEnabled = -1;
+ if (NO_ERROR == param.get(key, value)) {
+ if (value == LifevibesEnable) {
+ musicEnabled = 1;
+ } else if (value == LifevibesDisable) {
+ musicEnabled = 0;
+ }
+ }
+#endif
+
+ // ioHandle == 0 means the parameters are global to the audio hardware interface
+ if (ioHandle == 0) {
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_SET_PARAMETER;
+ result = mAudioHardware->setParameters(keyValuePairs);
+#ifdef LVMX
+ if ((NO_ERROR == result) && (musicEnabled != -1)) {
+ LifeVibes::enableMusic((bool) musicEnabled);
+ }
+#endif
+ mHardwareStatus = AUDIO_HW_IDLE;
+ return result;
+ }
+
+ // hold a strong ref on thread in case closeOutput() or closeInput() is called
+ // and the thread is exited once the lock is released
+ sp<ThreadBase> thread;
+ {
+ Mutex::Autolock _l(mLock);
+ thread = checkPlaybackThread_l(ioHandle);
+ if (thread == NULL) {
+ thread = checkRecordThread_l(ioHandle);
+ }
+ }
+ if (thread != NULL) {
+ result = thread->setParameters(keyValuePairs);
+#ifdef LVMX
+ if ((NO_ERROR == result) && (device != -1)) {
+ LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device);
+ }
+#endif
+ return result;
+ }
+ return BAD_VALUE;
+}
+
+String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
+{
+// LOGV("getParameters() io %d, keys %s, tid %d, calling tid %d",
+// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
+
+ if (ioHandle == 0) {
+ return mAudioHardware->getParameters(keys);
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle);
+ if (playbackThread != NULL) {
+ return playbackThread->getParameters(keys);
+ }
+ RecordThread *recordThread = checkRecordThread_l(ioHandle);
+ if (recordThread != NULL) {
+ return recordThread->getParameters(keys);
+ }
+ return String8("");
+}
+
+size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+ return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount);
+}
+
+unsigned int AudioFlinger::getInputFramesLost(int ioHandle)
+{
+ if (ioHandle == 0) {
+ return 0;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ RecordThread *recordThread = checkRecordThread_l(ioHandle);
+ if (recordThread != NULL) {
+ return recordThread->getInputFramesLost();
+ }
+ return 0;
+}
+
+status_t AudioFlinger::setVoiceVolume(float value)
+{
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
+ }
+
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
+ status_t ret = mAudioHardware->setVoiceVolume(value);
+ mHardwareStatus = AUDIO_HW_IDLE;
+
+ return ret;
+}
+
+status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output)
+{
+ status_t status;
+
+ Mutex::Autolock _l(mLock);
+
+ PlaybackThread *playbackThread = checkPlaybackThread_l(output);
+ if (playbackThread != NULL) {
+ return playbackThread->getRenderPosition(halFrames, dspFrames);
+ }
+
+ return BAD_VALUE;
+}
+
+void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
+{
+
+ LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid());
+ Mutex::Autolock _l(mLock);
+
+ sp<IBinder> binder = client->asBinder();
+ if (mNotificationClients.indexOf(binder) < 0) {
+ LOGV("Adding notification client %p", binder.get());
+ binder->linkToDeath(this);
+ mNotificationClients.add(binder);
+ }
+
+ // the config change is always sent from playback or record threads to avoid deadlock
+ // with AudioSystem::gLock
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+ }
+
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
+ }
+}
+
+void AudioFlinger::binderDied(const wp<IBinder>& who) {
+
+ LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
+ Mutex::Autolock _l(mLock);
+
+ IBinder *binder = who.unsafe_get();
+
+ if (binder != NULL) {
+ int index = mNotificationClients.indexOf(binder);
+ if (index >= 0) {
+ LOGV("Removing notification client %p", binder);
+ mNotificationClients.removeAt(index);
+ }
+ }
+}
+
+// audioConfigChanged_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) {
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<IBinder> binder = mNotificationClients.itemAt(i);
+ LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get());
+ sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
+ client->ioConfigChanged(event, ioHandle, param2);
+ }
+}
+
+// removeClient_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::removeClient_l(pid_t pid)
+{
+ LOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
+ mClients.removeItem(pid);
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id)
+ : Thread(false),
+ mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
+ mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false)
+{
+}
+
+AudioFlinger::ThreadBase::~ThreadBase()
+{
+ mParamCond.broadcast();
+ mNewParameters.clear();
+}
+
+void AudioFlinger::ThreadBase::exit()
+{
+ // keep a strong ref on ourself so that we wont get
+ // destroyed in the middle of requestExitAndWait()
+ sp <ThreadBase> strongMe = this;
+
+ LOGV("ThreadBase::exit");
+ {
+ AutoMutex lock(&mLock);
+ mExiting = true;
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+uint32_t AudioFlinger::ThreadBase::sampleRate() const
+{
+ return mSampleRate;
+}
+
+int AudioFlinger::ThreadBase::channelCount() const
+{
+ return mChannelCount;
+}
+
+int AudioFlinger::ThreadBase::format() const
+{
+ return mFormat;
+}
+
+size_t AudioFlinger::ThreadBase::frameCount() const
+{
+ return mFrameCount;
+}
+
+status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
+{
+ status_t status;
+
+ LOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
+ Mutex::Autolock _l(mLock);
+
+ mNewParameters.add(keyValuePairs);
+ mWaitWorkCV.signal();
+ // wait condition with timeout in case the thread loop has exited
+ // before the request could be processed
+ if (mParamCond.waitRelative(mLock, seconds(2)) == NO_ERROR) {
+ status = mParamStatus;
+ mWaitWorkCV.signal();
+ } else {
+ status = TIMED_OUT;
+ }
+ return status;
+}
+
+void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param)
+{
+ Mutex::Autolock _l(mLock);
+ sendConfigEvent_l(event, param);
+}
+
+// sendConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
+{
+ ConfigEvent *configEvent = new ConfigEvent();
+ configEvent->mEvent = event;
+ configEvent->mParam = param;
+ mConfigEvents.add(configEvent);
+ LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
+ mWaitWorkCV.signal();
+}
+
+void AudioFlinger::ThreadBase::processConfigEvents()
+{
+ mLock.lock();
+ while(!mConfigEvents.isEmpty()) {
+ LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
+ ConfigEvent *configEvent = mConfigEvents[0];
+ mConfigEvents.removeAt(0);
+ // release mLock because audioConfigChanged() will lock AudioFlinger mLock
+ // before calling Audioflinger::audioConfigChanged_l() thus creating
+ // potential cross deadlock between AudioFlinger::mLock and mLock
+ mLock.unlock();
+ audioConfigChanged(configEvent->mEvent, configEvent->mParam);
+ delete configEvent;
+ mLock.lock();
+ }
+ mLock.unlock();
+}
+
+status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this);
+ write(fd, buffer, strlen(buffer));
+ }
+
+ snprintf(buffer, SIZE, "standby: %d\n", mStandby);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Frame count: %d\n", mFrameCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
+ result.append(buffer);
+ result.append(" Index Command");
+ for (size_t i = 0; i < mNewParameters.size(); ++i) {
+ snprintf(buffer, SIZE, "\n %02d ", i);
+ result.append(buffer);
+ result.append(mNewParameters[i]);
+ }
+
+ snprintf(buffer, SIZE, "\n\nPending config events: \n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Index event param\n");
+ result.append(buffer);
+ for (size_t i = 0; i < mConfigEvents.size(); i++) {
+ snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i]->mEvent, mConfigEvents[i]->mParam);
+ result.append(buffer);
+ }
+ result.append("\n");
+
+ write(fd, result.string(), result.size());
+
+ if (locked) {
+ mLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : ThreadBase(audioFlinger, id),
+ mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
+ mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
+{
+ readOutputParameters();
+
+ mMasterVolume = mAudioFlinger->masterVolume();
+ mMasterMute = mAudioFlinger->masterMute();
+
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
+ mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+ }
+ // notify client processes that a new input has been opened
+ sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+}
+
+AudioFlinger::PlaybackThread::~PlaybackThread()
+{
+ delete [] mMixBuffer;
+}
+
+status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ dumpTracks(fd, args);
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
+ result.append(buffer);
+ result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
+ result.append(buffer);
+ result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
+ for (size_t i = 0; i < mActiveTracks.size(); ++i) {
+ wp<Track> wTrack = mActiveTracks[i];
+ if (wTrack != 0) {
+ sp<Track> track = wTrack.promote();
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+ result.append(buffer);
+ snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ dumpBase(fd, args);
+
+ return NO_ERROR;
+}
+
+// Thread virtuals
+status_t AudioFlinger::PlaybackThread::readyToRun()
+{
+ if (mSampleRate == 0) {
+ LOGE("No working audio driver found.");
+ return NO_INIT;
+ }
+ LOGI("AudioFlinger's thread %p ready to run", this);
+ return NO_ERROR;
+}
+
+void AudioFlinger::PlaybackThread::onFirstRef()
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "Playback Thread %p", this);
+
+ run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ const sp<IMemory>& sharedBuffer,
+ status_t *status)
+{
+ sp<Track> track;
+ status_t lStatus;
+
+ if (mType == DIRECT) {
+ if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) {
+ LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p",
+ sampleRate, format, channelCount, mOutput);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ } else {
+ // Resampler implementation limits input sampling rate to 2 x output sampling rate.
+ if (sampleRate > mSampleRate*2) {
+ LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+
+ if (mOutput == 0) {
+ LOGE("Audio driver not initialized.");
+ lStatus = NO_INIT;
+ goto Exit;
+ }
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ track = new Track(this, client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer);
+ if (track->getCblk() == NULL || track->name() < 0) {
+ lStatus = NO_MEMORY;
+ goto Exit;
+ }
+ mTracks.add(track);
+ }
+ lStatus = NO_ERROR;
+
+Exit:
+ if(status) {
+ *status = lStatus;
+ }
+ return track;
+}
+
+uint32_t AudioFlinger::PlaybackThread::latency() const
+{
+ if (mOutput) {
+ return mOutput->latency();
+ }
+ else {
+ return 0;
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
+{
+#ifdef LVMX
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+ LifeVibes::setMasterVolume(audioOutputType, value);
+ }
+#endif
+ mMasterVolume = value;
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
+{
+#ifdef LVMX
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+ LifeVibes::setMasterMute(audioOutputType, muted);
+ }
+#endif
+ mMasterMute = muted;
+ return NO_ERROR;
+}
+
+float AudioFlinger::PlaybackThread::masterVolume() const
+{
+ return mMasterVolume;
+}
+
+bool AudioFlinger::PlaybackThread::masterMute() const
+{
+ return mMasterMute;
+}
+
+status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
+{
+#ifdef LVMX
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+ LifeVibes::setStreamVolume(audioOutputType, stream, value);
+ }
+#endif
+ mStreamTypes[stream].volume = value;
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted)
+{
+#ifdef LVMX
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+ LifeVibes::setStreamMute(audioOutputType, stream, muted);
+ }
+#endif
+ mStreamTypes[stream].mute = muted;
+ return NO_ERROR;
+}
+
+float AudioFlinger::PlaybackThread::streamVolume(int stream) const
+{
+ return mStreamTypes[stream].volume;
+}
+
+bool AudioFlinger::PlaybackThread::streamMute(int stream) const
+{
+ return mStreamTypes[stream].mute;
+}
+
+bool AudioFlinger::PlaybackThread::isStreamActive(int stream) const
+{
+ Mutex::Autolock _l(mLock);
+ size_t count = mActiveTracks.size();
+ for (size_t i = 0 ; i < count ; ++i) {
+ sp<Track> t = mActiveTracks[i].promote();
+ if (t == 0) continue;
+ Track* const track = t.get();
+ if (t->type() == stream)
+ return true;
+ }
+ return false;
+}
+
+// addTrack_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
+{
+ status_t status = ALREADY_EXISTS;
+
+ // set retry count for buffer fill
+ track->mRetryCount = kMaxTrackStartupRetries;
+ if (mActiveTracks.indexOf(track) < 0) {
+ // the track is newly added, make sure it fills up all its
+ // buffers before playing. This is to ensure the client will
+ // effectively get the latency it requested.
+ track->mFillingUpStatus = Track::FS_FILLING;
+ track->mResetDone = false;
+ mActiveTracks.add(track);
+ status = NO_ERROR;
+ }
+
+ LOGV("mWaitWorkCV.broadcast");
+ mWaitWorkCV.broadcast();
+
+ return status;
+}
+
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
+{
+ track->mState = TrackBase::TERMINATED;
+ if (mActiveTracks.indexOf(track) < 0) {
+ mTracks.remove(track);
+ deleteTrackName_l(track->name());
+ }
+}
+
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
+ return mOutput->getParameters(keys);
+}
+
+void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = 0;
+
+ LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param);
+
+ switch (event) {
+ case AudioSystem::OUTPUT_OPENED:
+ case AudioSystem::OUTPUT_CONFIG_CHANGED:
+ desc.channels = mChannelCount;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mFrameCount;
+ desc.latency = latency();
+ param2 = &desc;
+ break;
+
+ case AudioSystem::STREAM_CONFIG_CHANGED:
+ param2 = &param;
+ case AudioSystem::OUTPUT_CLOSED:
+ default:
+ break;
+ }
+ Mutex::Autolock _l(mAudioFlinger->mLock);
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+}
+
+void AudioFlinger::PlaybackThread::readOutputParameters()
+{
+ mSampleRate = mOutput->sampleRate();
+ mChannelCount = AudioSystem::popCount(mOutput->channels());
+
+ mFormat = mOutput->format();
+ mFrameSize = mOutput->frameSize();
+ mFrameCount = mOutput->bufferSize() / mFrameSize;
+
+ // FIXME - Current mixer implementation only supports stereo output: Always
+ // Allocate a stereo buffer even if HW output is mono.
+ if (mMixBuffer != NULL) delete mMixBuffer;
+ mMixBuffer = new int16_t[mFrameCount * 2];
+ memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+}
+
+status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
+{
+ if (halFrames == 0 || dspFrames == 0) {
+ return BAD_VALUE;
+ }
+ if (mOutput == 0) {
+ return INVALID_OPERATION;
+ }
+ *halFrames = mBytesWritten/mOutput->frameSize();
+
+ return mOutput->getRenderPosition(dspFrames);
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : PlaybackThread(audioFlinger, output, id),
+ mAudioMixer(0)
+{
+ mType = PlaybackThread::MIXER;
+ mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+
+ // FIXME - Current mixer implementation only supports stereo output
+ if (mChannelCount == 1) {
+ LOGE("Invalid audio hardware channel count");
+ }
+}
+
+AudioFlinger::MixerThread::~MixerThread()
+{
+ delete mAudioMixer;
+}
+
+bool AudioFlinger::MixerThread::threadLoop()
+{
+ int16_t* curBuf = mMixBuffer;
+ Vector< sp<Track> > tracksToRemove;
+ uint32_t mixerStatus = MIXER_IDLE;
+ nsecs_t standbyTime = systemTime();
+ size_t mixBufferSize = mFrameCount * mFrameSize;
+ // FIXME: Relaxed timing because of a certain device that can't meet latency
+ // Should be reduced to 2x after the vendor fixes the driver issue
+ nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
+ nsecs_t lastWarning = 0;
+ bool longStandbyExit = false;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ mixerStatus = MIXER_IDLE;
+ { // scope for mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount * mFrameSize;
+ // FIXME: Relaxed timing because of a certain device that can't meet latency
+ // Should be reduced to 2x after the vendor fixes the driver issue
+ maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
+ }
+
+ const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ if (!mStandby) {
+ LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+
+ if (exitPending()) break;
+
+ // wait until we have something to do...
+ LOGV("MixerThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("MixerThread %p TID %d waking up\n", this, gettid());
+
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ sleepTime = idleSleepTime;
+ continue;
+ }
+ }
+
+ mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+ }
+
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ // mix buffers...
+ mAudioMixer->process(curBuf);
+ sleepTime = 0;
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ } else {
+ // If no tracks are ready, sleep once for the duration of an output
+ // buffer size, then write 0s to the output
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 ||
+ (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
+ memset (curBuf, 0, mixBufferSize);
+ sleepTime = 0;
+ LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+ }
+ }
+
+ if (mSuspended) {
+ sleepTime = idleSleepTime;
+ }
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ mBytesWritten += mixBufferSize;
+#ifdef LVMX
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+ LifeVibes::process(audioOutputType, curBuf, mixBufferSize);
+ }
+#endif
+ int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
+ if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
+ mNumWrites++;
+ mInWrite = false;
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastWriteTime;
+ if (delta > maxPeriod) {
+ mNumDelayedWrites++;
+ if ((now - lastWarning) > kWarningThrottle) {
+ LOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+ ns2ms(delta), mNumDelayedWrites, this);
+ lastWarning = now;
+ }
+ if (mStandby) {
+ longStandbyExit = true;
+ }
+ }
+ mStandby = false;
+ } else {
+ usleep(sleepTime);
+ }
+
+ // finally let go of all our tracks, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ tracksToRemove.clear();
+ }
+
+ if (!mStandby) {
+ mOutput->standby();
+ }
+
+ LOGV("MixerThread %p exiting", this);
+ return false;
+}
+
+// prepareTracks_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+{
+
+ uint32_t mixerStatus = MIXER_IDLE;
+ // find out which tracks need to be processed
+ size_t count = activeTracks.size();
+
+ float masterVolume = mMasterVolume;
+ bool masterMute = mMasterMute;
+
+#ifdef LVMX
+ bool tracksConnectedChanged = false;
+ bool stateChanged = false;
+
+ int audioOutputType = LifeVibes::getMixerType(mId, mType);
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
+ {
+ int activeTypes = 0;
+ for (size_t i=0 ; i<count ; i++) {
+ sp<Track> t = activeTracks[i].promote();
+ if (t == 0) continue;
+ Track* const track = t.get();
+ int iTracktype=track->type();
+ activeTypes |= 1<<track->type();
+ }
+ LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
+ }
+#endif
+
+ for (size_t i=0 ; i<count ; i++) {
+ sp<Track> t = activeTracks[i].promote();
+ if (t == 0) continue;
+
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ mAudioMixer->setActiveTrack(track->name());
+ if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
+ !track->isPaused() && !track->isTerminated())
+ {
+ //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
+
+ // compute volume for this track
+ int16_t left, right;
+ if (track->isMuted() || masterMute || track->isPausing() ||
+ mStreamTypes[track->type()].mute) {
+ left = right = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+ // read original volumes with volume control
+ float typeVolume = mStreamTypes[track->type()].volume;
+#ifdef LVMX
+ bool streamMute=false;
+ // read the volume from the LivesVibes audio engine.
+ if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
+ {
+ LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute);
+ if (streamMute) {
+ typeVolume = 0;
+ }
+ }
+#endif
+ float v = masterVolume * typeVolume;
+ float v_clamped = v * cblk->volume[0];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = int16_t(v_clamped);
+ v_clamped = v * cblk->volume[1];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = int16_t(v_clamped);
+ }
+
+ // XXX: these things DON'T need to be done each time
+ mAudioMixer->setBufferProvider(track);
+ mAudioMixer->enable(AudioMixer::MIXING);
+
+ int param = AudioMixer::VOLUME;
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ // no ramp for the first volume setting
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ param = AudioMixer::RAMP_VOLUME;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ param = AudioMixer::RAMP_VOLUME;
+ }
+#ifdef LVMX
+ if ( tracksConnectedChanged || stateChanged )
+ {
+ // only do the ramp when the volume is changed by the user / application
+ param = AudioMixer::VOLUME;
+ }
+#endif
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::FORMAT, track->format());
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::CHANNEL_COUNT, track->channelCount());
+ mAudioMixer->setParameter(
+ AudioMixer::RESAMPLE,
+ AudioMixer::SAMPLE_RATE,
+ int(cblk->sampleRate));
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetries;
+ mixerStatus = MIXER_TRACKS_READY;
+ } else {
+ //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this);
+ if (track->isStopped()) {
+ track->reset();
+ }
+ if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ tracksToRemove->add(track);
+ mAudioMixer->disable(AudioMixer::MIXING);
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
+ tracksToRemove->add(track);
+ } else if (mixerStatus != MIXER_TRACKS_READY) {
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+
+ mAudioMixer->disable(AudioMixer::MIXING);
+ }
+ }
+ }
+
+ // remove all the tracks that need to be...
+ count = tracksToRemove->size();
+ if (UNLIKELY(count)) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track>& track = tracksToRemove->itemAt(i);
+ mActiveTracks.remove(track);
+ if (track->isTerminated()) {
+ mTracks.remove(track);
+ deleteTrackName_l(track->mName);
+ }
+ }
+ }
+
+ return mixerStatus;
+}
+
+void AudioFlinger::MixerThread::getTracks(
+ SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks,
+ int streamType)
+{
+ LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size());
+ Mutex::Autolock _l(mLock);
+ size_t size = mTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = mTracks[i];
+ if (t->type() == streamType) {
+ tracks.add(t);
+ int j = mActiveTracks.indexOf(t);
+ if (j >= 0) {
+ t = mActiveTracks[j].promote();
+ if (t != NULL) {
+ activeTracks.add(t);
+ }
+ }
+ }
+ }
+
+ size = activeTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ mActiveTracks.remove(activeTracks[i]);
+ }
+
+ size = tracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = tracks[i];
+ mTracks.remove(t);
+ deleteTrackName_l(t->name());
+ }
+}
+
+void AudioFlinger::MixerThread::putTracks(
+ SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks)
+{
+ LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size());
+ Mutex::Autolock _l(mLock);
+ size_t size = tracks.size();
+ for (size_t i = 0; i < size ; i++) {
+ sp<Track> t = tracks[i];
+ int name = getTrackName_l();
+
+ if (name < 0) return;
+
+ t->mName = name;
+ t->mThread = this;
+ mTracks.add(t);
+
+ int j = activeTracks.indexOf(t);
+ if (j >= 0) {
+ mActiveTracks.add(t);
+ // force buffer refilling and no ramp volume when the track is mixed for the first time
+ t->mFillingUpStatus = Track::FS_FILLING;
+ }
+ }
+}
+
+// getTrackName_l() must be called with ThreadBase::mLock held
+int AudioFlinger::MixerThread::getTrackName_l()
+{
+ return mAudioMixer->getTrackName();
+}
+
+// deleteTrackName_l() must be called with ThreadBase::mLock held
+void AudioFlinger::MixerThread::deleteTrackName_l(int name)
+{
+ LOGV("remove track (%d) and delete from mixer", name);
+ mAudioMixer->deleteTrackName(name);
+}
+
+// checkForNewParameters_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if (value != AudioSystem::PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ if (value != AudioSystem::CHANNEL_OUT_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->setParameters(keyValuePair);
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->setParameters(keyValuePair);
+ }
+ if (status == NO_ERROR && reconfig) {
+ delete mAudioMixer;
+ readOutputParameters();
+ mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ int name = getTrackName_l();
+ if (name < 0) break;
+ mTracks[i]->mName = name;
+ // limit track sample rate to 2 x new output sample rate
+ if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
+ mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
+ }
+ }
+ sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ PlaybackThread::dumpInternals(fd, args);
+
+ snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
+{
+ return (uint32_t)(mOutput->latency() * 1000) / 2;
+}
+
+uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
+{
+ return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+}
+
+// ----------------------------------------------------------------------------
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : PlaybackThread(audioFlinger, output, id),
+ mLeftVolume (1.0), mRightVolume(1.0)
+{
+ mType = PlaybackThread::DIRECT;
+}
+
+AudioFlinger::DirectOutputThread::~DirectOutputThread()
+{
+}
+
+
+bool AudioFlinger::DirectOutputThread::threadLoop()
+{
+ uint32_t mixerStatus = MIXER_IDLE;
+ sp<Track> trackToRemove;
+ sp<Track> activeTrack;
+ nsecs_t standbyTime = systemTime();
+ int8_t *curBuf;
+ size_t mixBufferSize = mFrameCount*mFrameSize;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
+ // use shorter standby delay as on normal output to release
+ // hardware resources as soon as possible
+ nsecs_t standbyDelay = microseconds(activeSleepTime*2);
+
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ mixerStatus = MIXER_IDLE;
+
+ { // scope for the mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount*mFrameSize;
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
+ standbyDelay = microseconds(activeSleepTime*2);
+ }
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ // wait until we have something to do...
+ if (!mStandby) {
+ LOGV("Audio hardware entering standby, mixer %p\n", this);
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+
+ if (exitPending()) break;
+
+ LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid());
+
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + standbyDelay;
+ sleepTime = idleSleepTime;
+ continue;
+ }
+ }
+
+ // find out which tracks need to be processed
+ if (mActiveTracks.size() != 0) {
+ sp<Track> t = mActiveTracks[0].promote();
+ if (t == 0) continue;
+
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
+ !track->isPaused() && !track->isTerminated())
+ {
+ //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+
+ // compute volume for this track
+ float left, right;
+ if (track->isMuted() || mMasterMute || track->isPausing() ||
+ mStreamTypes[track->type()].mute) {
+ left = right = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+ float typeVolume = mStreamTypes[track->type()].volume;
+ float v = mMasterVolume * typeVolume;
+ float v_clamped = v * cblk->volume[0];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * cblk->volume[1];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
+
+ if (left != mLeftVolume || right != mRightVolume) {
+ mOutput->setVolume(left, right);
+ left = mLeftVolume;
+ right = mRightVolume;
+ }
+
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ }
+ }
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesDirect;
+ activeTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ } else {
+ //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ if (track->isStopped()) {
+ track->reset();
+ }
+ if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ trackToRemove = track;
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ trackToRemove = track;
+ } else {
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ }
+ }
+
+ // remove all the tracks that need to be...
+ if (UNLIKELY(trackToRemove != 0)) {
+ mActiveTracks.remove(trackToRemove);
+ if (trackToRemove->isTerminated()) {
+ mTracks.remove(trackToRemove);
+ deleteTrackName_l(trackToRemove->mName);
+ }
+ }
+ }
+
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ AudioBufferProvider::Buffer buffer;
+ size_t frameCount = mFrameCount;
+ curBuf = (int8_t *)mMixBuffer;
+ // output audio to hardware
+ while(frameCount) {
+ buffer.frameCount = frameCount;
+ activeTrack->getNextBuffer(&buffer);
+ if (UNLIKELY(buffer.raw == 0)) {
+ memset(curBuf, 0, frameCount * mFrameSize);
+ break;
+ }
+ memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
+ frameCount -= buffer.frameCount;
+ curBuf += buffer.frameCount * mFrameSize;
+ activeTrack->releaseBuffer(&buffer);
+ }
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ } else {
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
+ memset (mMixBuffer, 0, mFrameCount * mFrameSize);
+ sleepTime = 0;
+ }
+ }
+
+ if (mSuspended) {
+ sleepTime = idleSleepTime;
+ }
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ mBytesWritten += mixBufferSize;
+ int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
+ mNumWrites++;
+ mInWrite = false;
+ mStandby = false;
+ } else {
+ usleep(sleepTime);
+ }
+
+ // finally let go of removed track, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ trackToRemove.clear();
+ activeTrack.clear();
+ }
+
+ if (!mStandby) {
+ mOutput->standby();
+ }
+
+ LOGV("DirectOutputThread %p exiting", this);
+ return false;
+}
+
+// getTrackName_l() must be called with ThreadBase::mLock held
+int AudioFlinger::DirectOutputThread::getTrackName_l()
+{
+ return 0;
+}
+
+// deleteTrackName_l() must be called with ThreadBase::mLock held
+void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name)
+{
+}
+
+// checkForNewParameters_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->setParameters(keyValuePair);
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->setParameters(keyValuePair);
+ }
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters();
+ sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)(mOutput->latency() * 1000) / 2;
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX)
+{
+ mType = PlaybackThread::DUPLICATING;
+ addOutputTrack(mainThread);
+}
+
+AudioFlinger::DuplicatingThread::~DuplicatingThread()
+{
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ mOutputTracks[i]->destroy();
+ }
+ mOutputTracks.clear();
+}
+
+bool AudioFlinger::DuplicatingThread::threadLoop()
+{
+ int16_t* curBuf = mMixBuffer;
+ Vector< sp<Track> > tracksToRemove;
+ uint32_t mixerStatus = MIXER_IDLE;
+ nsecs_t standbyTime = systemTime();
+ size_t mixBufferSize = mFrameCount*mFrameSize;
+ SortedVector< sp<OutputTrack> > outputTracks;
+ uint32_t writeFrames = 0;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ mixerStatus = MIXER_IDLE;
+ { // scope for the mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount*mFrameSize;
+ updateWaitTime();
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
+ }
+
+ const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
+
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ outputTracks.add(mOutputTracks[i]);
+ }
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ if (!mStandby) {
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->stop();
+ }
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ outputTracks.clear();
+
+ if (exitPending()) break;
+
+ LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid());
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ sleepTime = idleSleepTime;
+ continue;
+ }
+ }
+
+ mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+ }
+
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+ // mix buffers...
+ if (outputsReady(outputTracks)) {
+ mAudioMixer->process(curBuf);
+ } else {
+ memset(curBuf, 0, mixBufferSize);
+ }
+ sleepTime = 0;
+ writeFrames = mFrameCount;
+ } else {
+ if (sleepTime == 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0) {
+ // flush remaining overflow buffers in output tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ if (outputTracks[i]->isActive()) {
+ sleepTime = 0;
+ writeFrames = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ if (mSuspended) {
+ sleepTime = idleSleepTime;
+ }
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->write(curBuf, writeFrames);
+ }
+ mStandby = false;
+ mBytesWritten += mixBufferSize;
+ } else {
+ usleep(sleepTime);
+ }
+
+ // finally let go of all our tracks, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ tracksToRemove.clear();
+ outputTracks.clear();
+ }
+
+ return false;
+}
+
+void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
+{
+ int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
+ OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread,
+ this,
+ mSampleRate,
+ mFormat,
+ mChannelCount,
+ frameCount);
+ if (outputTrack->cblk() != NULL) {
+ thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
+ mOutputTracks.add(outputTrack);
+ LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
+ updateWaitTime();
+ }
+}
+
+void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ if (mOutputTracks[i]->thread() == (ThreadBase *)thread) {
+ mOutputTracks[i]->destroy();
+ mOutputTracks.removeAt(i);
+ updateWaitTime();
+ return;
+ }
+ }
+ LOGV("removeOutputTrack(): unkonwn thread: %p", thread);
+}
+
+void AudioFlinger::DuplicatingThread::updateWaitTime()
+{
+ mWaitTimeMs = UINT_MAX;
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ sp<ThreadBase> strong = mOutputTracks[i]->thread().promote();
+ if (strong != NULL) {
+ uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate();
+ if (waitTimeMs < mWaitTimeMs) {
+ mWaitTimeMs = waitTimeMs;
+ }
+ }
+ }
+}
+
+
+bool AudioFlinger::DuplicatingThread::outputsReady(SortedVector< sp<OutputTrack> > &outputTracks)
+{
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ sp <ThreadBase> thread = outputTracks[i]->thread().promote();
+ if (thread == 0) {
+ LOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get());
+ return false;
+ }
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->standby() && !playbackThread->isSuspended()) {
+ LOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get());
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs()
+{
+ return (mWaitTimeMs * 1000) / 2;
+}
+
+// ----------------------------------------------------------------------------
+
+// TrackBase constructor must be called with AudioFlinger::mLock held
+AudioFlinger::ThreadBase::TrackBase::TrackBase(
+ const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer)
+ : RefBase(),
+ mThread(thread),
+ mClient(client),
+ mCblk(0),
+ mFrameCount(0),
+ mState(IDLE),
+ mClientTid(-1),
+ mFormat(format),
+ mFlags(flags & ~SYSTEM_FLAGS_MASK)
+{
+ LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
+
+ // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
+ size_t size = sizeof(audio_track_cblk_t);
+ size_t bufferSize = frameCount*channelCount*sizeof(int16_t);
+ if (sharedBuffer == 0) {
+ size += bufferSize;
+ }
+
+ if (client != NULL) {
+ mCblkMemory = client->heap()->allocate(size);
+ if (mCblkMemory != 0) {
+ mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
+ if (mCblk) { // construct the shared structure in-place.
+ new(mCblk) audio_track_cblk_t();
+ // clear all buffers
+ mCblk->frameCount = frameCount;
+ mCblk->sampleRate = sampleRate;
+ mCblk->channels = (uint8_t)channelCount;
+ if (sharedBuffer == 0) {
+ mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+ memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
+ // Force underrun condition to avoid false underrun callback until first data is
+ // written to buffer
+ mCblk->flowControlFlag = 1;
+ } else {
+ mBuffer = sharedBuffer->pointer();
+ }
+ mBufferEnd = (uint8_t *)mBuffer + bufferSize;
+ }
+ } else {
+ LOGE("not enough memory for AudioTrack size=%u", size);
+ client->heap()->dump("AudioTrack");
+ return;
+ }
+ } else {
+ mCblk = (audio_track_cblk_t *)(new uint8_t[size]);
+ if (mCblk) { // construct the shared structure in-place.
+ new(mCblk) audio_track_cblk_t();
+ // clear all buffers
+ mCblk->frameCount = frameCount;
+ mCblk->sampleRate = sampleRate;
+ mCblk->channels = (uint8_t)channelCount;
+ mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+ memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
+ // Force underrun condition to avoid false underrun callback until first data is
+ // written to buffer
+ mCblk->flowControlFlag = 1;
+ mBufferEnd = (uint8_t *)mBuffer + bufferSize;
+ }
+ }
+}
+
+AudioFlinger::ThreadBase::TrackBase::~TrackBase()
+{
+ if (mCblk) {
+ mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
+ if (mClient == NULL) {
+ delete mCblk;
+ }
+ }
+ mCblkMemory.clear(); // and free the shared memory
+ if (mClient != NULL) {
+ Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+ mClient.clear();
+ }
+}
+
+void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ buffer->raw = 0;
+ mFrameCount = buffer->frameCount;
+ step();
+ buffer->frameCount = 0;
+}
+
+bool AudioFlinger::ThreadBase::TrackBase::step() {
+ bool result;
+ audio_track_cblk_t* cblk = this->cblk();
+
+ result = cblk->stepServer(mFrameCount);
+ if (!result) {
+ LOGV("stepServer failed acquiring cblk mutex");
+ mFlags |= STEPSERVER_FAILED;
+ }
+ return result;
+}
+
+void AudioFlinger::ThreadBase::TrackBase::reset() {
+ audio_track_cblk_t* cblk = this->cblk();
+
+ cblk->user = 0;
+ cblk->server = 0;
+ cblk->userBase = 0;
+ cblk->serverBase = 0;
+ mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK);
+ LOGV("TrackBase::reset");
+}
+
+sp<IMemory> AudioFlinger::ThreadBase::TrackBase::getCblk() const
+{
+ return mCblkMemory;
+}
+
+int AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
+ return (int)mCblk->sampleRate;
+}
+
+int AudioFlinger::ThreadBase::TrackBase::channelCount() const {
+ return (int)mCblk->channels;
+}
+
+void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
+ audio_track_cblk_t* cblk = this->cblk();
+ int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize;
+ int8_t *bufferEnd = bufferStart + frames * cblk->frameSize;
+
+ // Check validity of returned pointer in case the track control block would have been corrupted.
+ if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
+ ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) {
+ LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \
+ server %d, serverBase %d, user %d, userBase %d, channels %d",
+ bufferStart, bufferEnd, mBuffer, mBufferEnd,
+ cblk->server, cblk->serverBase, cblk->user, cblk->userBase, cblk->channels);
+ return 0;
+ }
+
+ return bufferStart;
+}
+
+// ----------------------------------------------------------------------------
+
+// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
+AudioFlinger::PlaybackThread::Track::Track(
+ const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ const sp<IMemory>& sharedBuffer)
+ : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
+ mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
+{
+ if (mCblk != NULL) {
+ sp<ThreadBase> baseThread = thread.promote();
+ if (baseThread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
+ mName = playbackThread->getTrackName_l();
+ }
+ LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ if (mName < 0) {
+ LOGE("no more track names available");
+ }
+ mVolume[0] = 1.0f;
+ mVolume[1] = 1.0f;
+ mStreamType = streamType;
+ // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
+ // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
+ mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
+ }
+}
+
+AudioFlinger::PlaybackThread::Track::~Track()
+{
+ LOGV("PlaybackThread::Track destructor");
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ mState = TERMINATED;
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::destroy()
+{
+ // NOTE: destroyTrack_l() can remove a strong reference to this Track
+ // by removing it from mTracks vector, so there is a risk that this Tracks's
+ // desctructor is called. As the destructor needs to lock mLock,
+ // we must acquire a strong reference on this Track before locking mLock
+ // here so that the destructor is called only when exiting this function.
+ // On the other hand, as long as Track::destroy() is only called by
+ // TrackHandle destructor, the TrackHandle still holds a strong ref on
+ // this Track with its member mTrack.
+ sp<Track> keep(this);
+ { // scope for mLock
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ if (!isOutputTrack()) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ }
+ AudioSystem::releaseOutput(thread->id());
+ }
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->destroyTrack_l(this);
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u %08x %08x\n",
+ mName - AudioMixer::TRACK0,
+ (mClient == NULL) ? getpid() : mClient->pid(),
+ mStreamType,
+ mFormat,
+ mCblk->channels,
+ mFrameCount,
+ mState,
+ mMute,
+ mFillingUpStatus,
+ mCblk->sampleRate,
+ mCblk->volume[0],
+ mCblk->volume[1],
+ mCblk->server,
+ mCblk->user);
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ audio_track_cblk_t* cblk = this->cblk();
+ uint32_t framesReady;
+ uint32_t framesReq = buffer->frameCount;
+
+ // Check if last stepServer failed, try to step now
+ if (mFlags & TrackBase::STEPSERVER_FAILED) {
+ if (!step()) goto getNextBuffer_exit;
+ LOGV("stepServer recovered");
+ mFlags &= ~TrackBase::STEPSERVER_FAILED;
+ }
+
+ framesReady = cblk->framesReady();
+
+ if (LIKELY(framesReady)) {
+ uint32_t s = cblk->server;
+ uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
+
+ bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
+ if (framesReq > framesReady) {
+ framesReq = framesReady;
+ }
+ if (s + framesReq > bufferEnd) {
+ framesReq = bufferEnd - s;
+ }
+
+ buffer->raw = getBuffer(s, framesReq);
+ if (buffer->raw == 0) goto getNextBuffer_exit;
+
+ buffer->frameCount = framesReq;
+ return NO_ERROR;
+ }
+
+getNextBuffer_exit:
+ buffer->raw = 0;
+ buffer->frameCount = 0;
+ LOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
+ return NOT_ENOUGH_DATA;
+}
+
+bool AudioFlinger::PlaybackThread::Track::isReady() const {
+ if (mFillingUpStatus != FS_FILLING) return true;
+
+ if (mCblk->framesReady() >= mCblk->frameCount ||
+ mCblk->forceReady) {
+ mFillingUpStatus = FS_FILLED;
+ mCblk->forceReady = 0;
+ return true;
+ }
+ return false;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::start()
+{
+ status_t status = NO_ERROR;
+ LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ int state = mState;
+ // here the track could be either new, or restarted
+ // in both cases "unstop" the track
+ if (mState == PAUSED) {
+ mState = TrackBase::RESUMING;
+ LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ } else {
+ mState = TrackBase::ACTIVE;
+ LOGV("? => ACTIVE (%d) on thread %p", mName, this);
+ }
+
+ if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
+ thread->mLock.unlock();
+ status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
+ if (status == NO_ERROR) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->addTrack_l(this);
+ } else {
+ mState = state;
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::stop()
+{
+ LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ int state = mState;
+ if (mState > STOPPED) {
+ mState = STOPPED;
+ // If the track is not active (PAUSED and buffers full), flush buffers
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
+ LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
+ }
+ if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
+ thread->mLock.unlock();
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::pause()
+{
+ LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ if (mState == ACTIVE || mState == RESUMING) {
+ mState = PAUSING;
+ LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
+ if (!isOutputTrack()) {
+ thread->mLock.unlock();
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::flush()
+{
+ LOGV("flush(%d)", mName);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
+ return;
+ }
+ // No point remaining in PAUSED state after a flush => go to
+ // STOPPED state
+ mState = STOPPED;
+
+ mCblk->lock.lock();
+ // NOTE: reset() will reset cblk->user and cblk->server with
+ // the risk that at the same time, the AudioMixer is trying to read
+ // data. In this case, getNextBuffer() would return a NULL pointer
+ // as audio buffer => the AudioMixer code MUST always test that pointer
+ // returned by getNextBuffer() is not NULL!
+ reset();
+ mCblk->lock.unlock();
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::reset()
+{
+ // Do not reset twice to avoid discarding data written just after a flush and before
+ // the audioflinger thread detects the track is stopped.
+ if (!mResetDone) {
+ TrackBase::reset();
+ // Force underrun condition to avoid false underrun callback until first data is
+ // written to buffer
+ mCblk->flowControlFlag = 1;
+ mCblk->forceReady = 0;
+ mFillingUpStatus = FS_FILLING;
+ mResetDone = true;
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::mute(bool muted)
+{
+ mMute = muted;
+}
+
+void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right)
+{
+ mVolume[0] = left;
+ mVolume[1] = right;
+}
+
+// ----------------------------------------------------------------------------
+
+// RecordTrack constructor must be called with AudioFlinger::mLock held
+AudioFlinger::RecordThread::RecordTrack::RecordTrack(
+ const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags)
+ : TrackBase(thread, client, sampleRate, format,
+ channelCount, frameCount, flags, 0),
+ mOverflow(false)
+{
+ if (mCblk != NULL) {
+ LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
+ if (format == AudioSystem::PCM_16_BIT) {
+ mCblk->frameSize = channelCount * sizeof(int16_t);
+ } else if (format == AudioSystem::PCM_8_BIT) {
+ mCblk->frameSize = channelCount * sizeof(int8_t);
+ } else {
+ mCblk->frameSize = sizeof(int8_t);
+ }
+ }
+}
+
+AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ AudioSystem::releaseInput(thread->id());
+ }
+}
+
+status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ audio_track_cblk_t* cblk = this->cblk();
+ uint32_t framesAvail;
+ uint32_t framesReq = buffer->frameCount;
+
+ // Check if last stepServer failed, try to step now
+ if (mFlags & TrackBase::STEPSERVER_FAILED) {
+ if (!step()) goto getNextBuffer_exit;
+ LOGV("stepServer recovered");
+ mFlags &= ~TrackBase::STEPSERVER_FAILED;
+ }
+
+ framesAvail = cblk->framesAvailable_l();
+
+ if (LIKELY(framesAvail)) {
+ uint32_t s = cblk->server;
+ uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
+
+ if (framesReq > framesAvail) {
+ framesReq = framesAvail;
+ }
+ if (s + framesReq > bufferEnd) {
+ framesReq = bufferEnd - s;
+ }
+
+ buffer->raw = getBuffer(s, framesReq);
+ if (buffer->raw == 0) goto getNextBuffer_exit;
+
+ buffer->frameCount = framesReq;
+ return NO_ERROR;
+ }
+
+getNextBuffer_exit:
+ buffer->raw = 0;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+}
+
+status_t AudioFlinger::RecordThread::RecordTrack::start()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ return recordThread->start(this);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::stop()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ recordThread->stop(this);
+ TrackBase::reset();
+ // Force overerrun condition to avoid false overrun callback until first data is
+ // read from buffer
+ mCblk->flowControlFlag = 1;
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n",
+ (mClient == NULL) ? getpid() : mClient->pid(),
+ mFormat,
+ mCblk->channels,
+ mFrameCount,
+ mState,
+ mCblk->sampleRate,
+ mCblk->server,
+ mCblk->user);
+}
+
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
+ const wp<ThreadBase>& thread,
+ DuplicatingThread *sourceThread,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount)
+ : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
+ mActive(false), mSourceThread(sourceThread)
+{
+
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get();
+ if (mCblk != NULL) {
+ mCblk->out = 1;
+ mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+ mCblk->volume[0] = mCblk->volume[1] = 0x1000;
+ mOutBuffer.frameCount = 0;
+ playbackThread->mTracks.add(this);
+ LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p",
+ mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd);
+ } else {
+ LOGW("Error creating output track on thread %p", playbackThread);
+ }
+}
+
+AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
+{
+ clearBufferQueue();
+}
+
+status_t AudioFlinger::PlaybackThread::OutputTrack::start()
+{
+ status_t status = Track::start();
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ mActive = true;
+ mRetryCount = 127;
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::OutputTrack::stop()
+{
+ Track::stop();
+ clearBufferQueue();
+ mOutBuffer.frameCount = 0;
+ mActive = false;
+}
+
+bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
+{
+ Buffer *pInBuffer;
+ Buffer inBuffer;
+ uint32_t channels = mCblk->channels;
+ bool outputBufferFull = false;
+ inBuffer.frameCount = frames;
+ inBuffer.i16 = data;
+
+ uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
+
+ if (!mActive && frames != 0) {
+ start();
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ MixerThread *mixerThread = (MixerThread *)thread.get();
+ if (mCblk->frameCount > frames){
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ uint32_t startFrames = (mCblk->frameCount - frames);
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[startFrames * channels];
+ pInBuffer->frameCount = startFrames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else {
+ LOGW ("OutputTrack::write() %p no more buffers in queue", this);
+ }
+ }
+ }
+ }
+
+ while (waitTimeLeftMs) {
+ // First write pending buffers, then new data
+ if (mBufferQueue.size()) {
+ pInBuffer = mBufferQueue.itemAt(0);
+ } else {
+ pInBuffer = &inBuffer;
+ }
+
+ if (pInBuffer->frameCount == 0) {
+ break;
+ }
+
+ if (mOutBuffer.frameCount == 0) {
+ mOutBuffer.frameCount = pInBuffer->frameCount;
+ nsecs_t startTime = systemTime();
+ if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) {
+ LOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get());
+ outputBufferFull = true;
+ break;
+ }
+ uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
+ if (waitTimeLeftMs >= waitTimeMs) {
+ waitTimeLeftMs -= waitTimeMs;
+ } else {
+ waitTimeLeftMs = 0;
+ }
+ }
+
+ uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount;
+ memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t));
+ mCblk->stepUser(outFrames);
+ pInBuffer->frameCount -= outFrames;
+ pInBuffer->i16 += outFrames * channels;
+ mOutBuffer.frameCount -= outFrames;
+ mOutBuffer.i16 += outFrames * channels;
+
+ if (pInBuffer->frameCount == 0) {
+ if (mBufferQueue.size()) {
+ mBufferQueue.removeAt(0);
+ delete [] pInBuffer->mBuffer;
+ delete pInBuffer;
+ LOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
+ } else {
+ break;
+ }
+ }
+ }
+
+ // If we could not write all frames, allocate a buffer and queue it for next time.
+ if (inBuffer.frameCount) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0 && !thread->standby()) {
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels];
+ pInBuffer->frameCount = inBuffer.frameCount;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ LOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
+ } else {
+ LOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this);
+ }
+ }
+ }
+
+ // Calling write() with a 0 length buffer, means that no more data will be written:
+ // If no more buffers are pending, fill output track buffer to make sure it is started
+ // by output mixer.
+ if (frames == 0 && mBufferQueue.size() == 0) {
+ if (mCblk->user < mCblk->frameCount) {
+ frames = mCblk->frameCount - mCblk->user;
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[frames * channels];
+ pInBuffer->frameCount = frames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else if (mActive) {
+ stop();
+ }
+ }
+
+ return outputBufferFull;
+}
+
+status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
+{
+ int active;
+ status_t result;
+ audio_track_cblk_t* cblk = mCblk;
+ uint32_t framesReq = buffer->frameCount;
+
+// LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
+ buffer->frameCount = 0;
+
+ uint32_t framesAvail = cblk->framesAvailable();
+
+
+ if (framesAvail == 0) {
+ Mutex::Autolock _l(cblk->lock);
+ goto start_loop_here;
+ while (framesAvail == 0) {
+ active = mActive;
+ if (UNLIKELY(!active)) {
+ LOGV("Not active and NO_MORE_BUFFERS");
+ return AudioTrack::NO_MORE_BUFFERS;
+ }
+ result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ if (result != NO_ERROR) {
+ return AudioTrack::NO_MORE_BUFFERS;
+ }
+ // read the server count again
+ start_loop_here:
+ framesAvail = cblk->framesAvailable_l();
+ }
+ }
+
+// if (framesAvail < framesReq) {
+// return AudioTrack::NO_MORE_BUFFERS;
+// }
+
+ if (framesReq > framesAvail) {
+ framesReq = framesAvail;
+ }
+
+ uint32_t u = cblk->user;
+ uint32_t bufferEnd = cblk->userBase + cblk->frameCount;
+
+ if (u + framesReq > bufferEnd) {
+ framesReq = bufferEnd - u;
+ }
+
+ buffer->frameCount = framesReq;
+ buffer->raw = (void *)cblk->buffer(u);
+ return NO_ERROR;
+}
+
+
+void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
+{
+ size_t size = mBufferQueue.size();
+ Buffer *pBuffer;
+
+ for (size_t i = 0; i < size; i++) {
+ pBuffer = mBufferQueue.itemAt(i);
+ delete [] pBuffer->mBuffer;
+ delete pBuffer;
+ }
+ mBufferQueue.clear();
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid)
+ : RefBase(),
+ mAudioFlinger(audioFlinger),
+ mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")),
+ mPid(pid)
+{
+ // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer
+}
+
+// Client destructor must be called with AudioFlinger::mLock held
+AudioFlinger::Client::~Client()
+{
+ mAudioFlinger->removeClient_l(mPid);
+}
+
+const sp<MemoryDealer>& AudioFlinger::Client::heap() const
+{
+ return mMemoryDealer;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track)
+ : BnAudioTrack(),
+ mTrack(track)
+{
+}
+
+AudioFlinger::TrackHandle::~TrackHandle() {
+ // just stop the track on deletion, associated resources
+ // will be freed from the main thread once all pending buffers have
+ // been played. Unless it's not in the active track list, in which
+ // case we free everything now...
+ mTrack->destroy();
+}
+
+status_t AudioFlinger::TrackHandle::start() {
+ return mTrack->start();
+}
+
+void AudioFlinger::TrackHandle::stop() {
+ mTrack->stop();
+}
+
+void AudioFlinger::TrackHandle::flush() {
+ mTrack->flush();
+}
+
+void AudioFlinger::TrackHandle::mute(bool e) {
+ mTrack->mute(e);
+}
+
+void AudioFlinger::TrackHandle::pause() {
+ mTrack->pause();
+}
+
+void AudioFlinger::TrackHandle::setVolume(float left, float right) {
+ mTrack->setVolume(left, right);
+}
+
+sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
+ return mTrack->getCblk();
+}
+
+status_t AudioFlinger::TrackHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioTrack::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+
+sp<IAudioRecord> AudioFlinger::openRecord(
+ pid_t pid,
+ int input,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ status_t *status)
+{
+ sp<RecordThread::RecordTrack> recordTrack;
+ sp<RecordHandle> recordHandle;
+ sp<Client> client;
+ wp<Client> wclient;
+ status_t lStatus;
+ RecordThread *thread;
+ size_t inFrameCount;
+
+ // check calling permissions
+ if (!recordingAllowed()) {
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+
+ // add client to list
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ thread = checkRecordThread_l(input);
+ if (thread == NULL) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ wclient = mClients.valueFor(pid);
+ if (wclient != NULL) {
+ client = wclient.promote();
+ } else {
+ client = new Client(this, pid);
+ mClients.add(pid, client);
+ }
+
+ // create new record track. The record track uses one track in mHardwareMixerThread by convention.
+ recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
+ format, channelCount, frameCount, flags);
+ }
+ if (recordTrack->getCblk() == NULL) {
+ // remove local strong reference to Client before deleting the RecordTrack so that the Client
+ // destructor is called by the TrackBase destructor with mLock held
+ client.clear();
+ recordTrack.clear();
+ lStatus = NO_MEMORY;
+ goto Exit;
+ }
+
+ // return to handle to client
+ recordHandle = new RecordHandle(recordTrack);
+ lStatus = NO_ERROR;
+
+Exit:
+ if (status) {
+ *status = lStatus;
+ }
+ return recordHandle;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack)
+ : BnAudioRecord(),
+ mRecordTrack(recordTrack)
+{
+}
+
+AudioFlinger::RecordHandle::~RecordHandle() {
+ stop();
+}
+
+status_t AudioFlinger::RecordHandle::start() {
+ LOGV("RecordHandle::start()");
+ return mRecordTrack->start();
+}
+
+void AudioFlinger::RecordHandle::stop() {
+ LOGV("RecordHandle::stop()");
+ mRecordTrack->stop();
+}
+
+sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
+ return mRecordTrack->getCblk();
+}
+
+status_t AudioFlinger::RecordHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioRecord::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels, int id) :
+ ThreadBase(audioFlinger, id),
+ mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
+{
+ mReqChannelCount = AudioSystem::popCount(channels);
+ mReqSampleRate = sampleRate;
+ readInputParameters();
+ sendConfigEvent(AudioSystem::INPUT_OPENED);
+}
+
+
+AudioFlinger::RecordThread::~RecordThread()
+{
+ delete[] mRsmpInBuffer;
+ if (mResampler != 0) {
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
+ }
+}
+
+void AudioFlinger::RecordThread::onFirstRef()
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "Record Thread %p", this);
+
+ run(buffer, PRIORITY_URGENT_AUDIO);
+}
+
+bool AudioFlinger::RecordThread::threadLoop()
+{
+ AudioBufferProvider::Buffer buffer;
+ sp<RecordTrack> activeTrack;
+
+ // start recording
+ while (!exitPending()) {
+
+ processConfigEvents();
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ checkForNewParameters_l();
+ if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
+ if (!mStandby) {
+ mInput->standby();
+ mStandby = true;
+ }
+
+ if (exitPending()) break;
+
+ LOGV("RecordThread: loop stopping");
+ // go to sleep
+ mWaitWorkCV.wait(mLock);
+ LOGV("RecordThread: loop starting");
+ continue;
+ }
+ if (mActiveTrack != 0) {
+ if (mActiveTrack->mState == TrackBase::PAUSING) {
+ if (!mStandby) {
+ mInput->standby();
+ mStandby = true;
+ }
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ } else if (mActiveTrack->mState == TrackBase::RESUMING) {
+ if (mReqChannelCount != mActiveTrack->channelCount()) {
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ } else if (mBytesRead != 0) {
+ // record start succeeds only if first read from audio input
+ // succeeds
+ if (mBytesRead > 0) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ } else {
+ mActiveTrack.clear();
+ }
+ mStartStopCond.broadcast();
+ }
+ mStandby = false;
+ }
+ }
+ }
+
+ if (mActiveTrack != 0) {
+ if (mActiveTrack->mState != TrackBase::ACTIVE &&
+ mActiveTrack->mState != TrackBase::RESUMING) {
+ usleep(5000);
+ continue;
+ }
+ buffer.frameCount = mFrameCount;
+ if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ size_t framesOut = buffer.frameCount;
+ if (mResampler == 0) {
+ // no resampling
+ while (framesOut) {
+ size_t framesIn = mFrameCount - mRsmpInIndex;
+ if (framesIn) {
+ int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
+ int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize;
+ if (framesIn > framesOut)
+ framesIn = framesOut;
+ mRsmpInIndex += framesIn;
+ framesOut -= framesIn;
+ if (mChannelCount == mReqChannelCount ||
+ mFormat != AudioSystem::PCM_16_BIT) {
+ memcpy(dst, src, framesIn * mFrameSize);
+ } else {
+ int16_t *src16 = (int16_t *)src;
+ int16_t *dst16 = (int16_t *)dst;
+ if (mChannelCount == 1) {
+ while (framesIn--) {
+ *dst16++ = *src16;
+ *dst16++ = *src16++;
+ }
+ } else {
+ while (framesIn--) {
+ *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1);
+ src16 += 2;
+ }
+ }
+ }
+ }
+ if (framesOut && mFrameCount == mRsmpInIndex) {
+ if (framesOut == mFrameCount &&
+ (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
+ mBytesRead = mInput->read(buffer.raw, mInputBytes);
+ framesOut = 0;
+ } else {
+ mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mRsmpInIndex = 0;
+ }
+ if (mBytesRead < 0) {
+ LOGE("Error reading audio input");
+ if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
+ }
+ mRsmpInIndex = mFrameCount;
+ framesOut = 0;
+ buffer.frameCount = 0;
+ }
+ }
+ }
+ } else {
+ // resampling
+
+ memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t));
+ // alter output frame count as if we were expecting stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ framesOut >>= 1;
+ }
+ mResampler->resample(mRsmpOutBuffer, framesOut, this);
+ // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
+ // are 32 bit aligned which should be always true.
+ if (mChannelCount == 2 && mReqChannelCount == 1) {
+ AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
+ // the resampler always outputs stereo samples: do post stereo to mono conversion
+ int16_t *src = (int16_t *)mRsmpOutBuffer;
+ int16_t *dst = buffer.i16;
+ while (framesOut--) {
+ *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1);
+ src += 2;
+ }
+ } else {
+ AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
+ }
+
+ }
+ mActiveTrack->releaseBuffer(&buffer);
+ mActiveTrack->overflow();
+ }
+ // client isn't retrieving buffers fast enough
+ else {
+ if (!mActiveTrack->setOverflow())
+ LOGW("RecordThread: buffer overflow");
+ // Release the processor for a while before asking for a new buffer.
+ // This will give the application more chance to read from the buffer and
+ // clear the overflow.
+ usleep(5000);
+ }
+ }
+ }
+
+ if (!mStandby) {
+ mInput->standby();
+ }
+ mActiveTrack.clear();
+
+ mStartStopCond.broadcast();
+
+ LOGV("RecordThread %p exiting", this);
+ return false;
+}
+
+status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack)
+{
+ LOGV("RecordThread::start");
+ sp <ThreadBase> strongMe = this;
+ status_t status = NO_ERROR;
+ {
+ AutoMutex lock(&mLock);
+ if (mActiveTrack != 0) {
+ if (recordTrack != mActiveTrack.get()) {
+ status = -EBUSY;
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ }
+ return status;
+ }
+
+ recordTrack->mState = TrackBase::IDLE;
+ mActiveTrack = recordTrack;
+ mLock.unlock();
+ status_t status = AudioSystem::startInput(mId);
+ mLock.lock();
+ if (status != NO_ERROR) {
+ mActiveTrack.clear();
+ return status;
+ }
+ mActiveTrack->mState = TrackBase::RESUMING;
+ mRsmpInIndex = mFrameCount;
+ mBytesRead = 0;
+ // signal thread to start
+ LOGV("Signal record thread");
+ mWaitWorkCV.signal();
+ // do not wait for mStartStopCond if exiting
+ if (mExiting) {
+ mActiveTrack.clear();
+ status = INVALID_OPERATION;
+ goto startError;
+ }
+ mStartStopCond.wait(mLock);
+ if (mActiveTrack == 0) {
+ LOGV("Record failed to start");
+ status = BAD_VALUE;
+ goto startError;
+ }
+ LOGV("Record started OK");
+ return status;
+ }
+startError:
+ AudioSystem::stopInput(mId);
+ return status;
+}
+
+void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
+ LOGV("RecordThread::stop");
+ sp <ThreadBase> strongMe = this;
+ {
+ AutoMutex lock(&mLock);
+ if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
+ mActiveTrack->mState = TrackBase::PAUSING;
+ // do not wait for mStartStopCond if exiting
+ if (mExiting) {
+ return;
+ }
+ mStartStopCond.wait(mLock);
+ // if we have been restarted, recordTrack == mActiveTrack.get() here
+ if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) {
+ mLock.unlock();
+ AudioSystem::stopInput(mId);
+ mLock.lock();
+ LOGV("Record stopped OK");
+ }
+ }
+ }
+}
+
+status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ pid_t pid = 0;
+
+ snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
+ result.append(buffer);
+
+ if (mActiveTrack != 0) {
+ result.append("Active Track:\n");
+ result.append(" Clien Fmt Chn Buf S SRate Serv User\n");
+ mActiveTrack->dump(buffer, SIZE);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != 0));
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Out channel count: %d\n", mReqChannelCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate);
+ result.append(buffer);
+
+
+ } else {
+ result.append("No record client\n");
+ }
+ write(fd, result.string(), result.size());
+
+ dumpBase(fd, args);
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ size_t framesReq = buffer->frameCount;
+ size_t framesReady = mFrameCount - mRsmpInIndex;
+ int channelCount;
+
+ if (framesReady == 0) {
+ mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ if (mBytesRead < 0) {
+ LOGE("RecordThread::getNextBuffer() Error reading audio input");
+ if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ mInput->standby();
+ usleep(5000);
+ }
+ buffer->raw = 0;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+ }
+ mRsmpInIndex = 0;
+ framesReady = mFrameCount;
+ }
+
+ if (framesReq > framesReady) {
+ framesReq = framesReady;
+ }
+
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
+ buffer->frameCount = framesReq;
+ return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ mRsmpInIndex += buffer->frameCount;
+ buffer->frameCount = 0;
+}
+
+bool AudioFlinger::RecordThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ int reqFormat = mFormat;
+ int reqSamplingRate = mReqSampleRate;
+ int reqChannelCount = mReqChannelCount;
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reqSamplingRate = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ reqFormat = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ reqChannelCount = AudioSystem::popCount(value);
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (mActiveTrack != 0) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mInput->setParameters(keyValuePair);
+ if (status == INVALID_OPERATION) {
+ mInput->standby();
+ status = mInput->setParameters(keyValuePair);
+ }
+ if (reconfig) {
+ if (status == BAD_VALUE &&
+ reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT &&
+ ((int)mInput->sampleRate() <= 2 * reqSamplingRate) &&
+ (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) {
+ status = NO_ERROR;
+ }
+ if (status == NO_ERROR) {
+ readInputParameters();
+ sendConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+ }
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
+{
+ return mInput->getParameters(keys);
+}
+
+void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = 0;
+
+ switch (event) {
+ case AudioSystem::INPUT_OPENED:
+ case AudioSystem::INPUT_CONFIG_CHANGED:
+ desc.channels = mChannelCount;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mFrameCount;
+ desc.latency = 0;
+ param2 = &desc;
+ break;
+
+ case AudioSystem::INPUT_CLOSED:
+ default:
+ break;
+ }
+ Mutex::Autolock _l(mAudioFlinger->mLock);
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+}
+
+void AudioFlinger::RecordThread::readInputParameters()
+{
+ if (mRsmpInBuffer) delete mRsmpInBuffer;
+ if (mRsmpOutBuffer) delete mRsmpOutBuffer;
+ if (mResampler) delete mResampler;
+ mResampler = 0;
+
+ mSampleRate = mInput->sampleRate();
+ mChannelCount = AudioSystem::popCount(mInput->channels());
+ mFormat = mInput->format();
+ mFrameSize = mInput->frameSize();
+ mInputBytes = mInput->bufferSize();
+ mFrameCount = mInputBytes / mFrameSize;
+ mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
+
+ if (mSampleRate != mReqSampleRate && mChannelCount < 3 && mReqChannelCount < 3)
+ {
+ int channelCount;
+ // optmization: if mono to mono, use the resampler in stereo to stereo mode to avoid
+ // stereo to mono post process as the resampler always outputs stereo.
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
+ mResampler->setSampleRate(mSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ mRsmpOutBuffer = new int32_t[mFrameCount * 2];
+
+ // optmization: if mono to mono, alter input frame count as if we were inputing stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ mFrameCount >>= 1;
+ }
+
+ }
+ mRsmpInIndex = mFrameCount;
+}
+
+unsigned int AudioFlinger::RecordThread::getInputFramesLost()
+{
+ return mInput->getInputFramesLost();
+}
+
+// ----------------------------------------------------------------------------
+
+int AudioFlinger::openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags)
+{
+ status_t status;
+ PlaybackThread *thread = NULL;
+ mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+ uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
+
+ LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
+ pDevices ? *pDevices : 0,
+ samplingRate,
+ format,
+ channels,
+ flags);
+
+ if (pDevices == NULL || *pDevices == 0) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+
+ AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status);
+ LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
+ output,
+ samplingRate,
+ format,
+ channels,
+ status);
+
+ mHardwareStatus = AUDIO_HW_IDLE;
+ if (output != 0) {
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format != AudioSystem::PCM_16_BIT) ||
+ (channels != AudioSystem::CHANNEL_OUT_STEREO)) {
+ thread = new DirectOutputThread(this, output, ++mNextThreadId);
+ LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread);
+ } else {
+ thread = new MixerThread(this, output, ++mNextThreadId);
+ LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread);
+
+#ifdef LVMX
+ unsigned bitsPerSample =
+ (format == AudioSystem::PCM_16_BIT) ? 16 :
+ ((format == AudioSystem::PCM_8_BIT) ? 8 : 0);
+ unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1;
+ int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id());
+
+ LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount);
+ LifeVibes::setDevice(audioOutputType, *pDevices);
+#endif
+
+ }
+ mPlaybackThreads.add(mNextThreadId, thread);
+
+ if (pSamplingRate) *pSamplingRate = samplingRate;
+ if (pFormat) *pFormat = format;
+ if (pChannels) *pChannels = channels;
+ if (pLatencyMs) *pLatencyMs = thread->latency();
+
+ return mNextThreadId;
+ }
+
+ return 0;
+}
+
+int AudioFlinger::openDuplicateOutput(int output1, int output2)
+{
+ Mutex::Autolock _l(mLock);
+ MixerThread *thread1 = checkMixerThread_l(output1);
+ MixerThread *thread2 = checkMixerThread_l(output2);
+
+ if (thread1 == NULL || thread2 == NULL) {
+ LOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2);
+ return 0;
+ }
+
+
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);
+ thread->addOutputTrack(thread2);
+ mPlaybackThreads.add(mNextThreadId, thread);
+ return mNextThreadId;
+}
+
+status_t AudioFlinger::closeOutput(int output)
+{
+ // keep strong reference on the playback thread so that
+ // it is not destroyed while exit() is executed
+ sp <PlaybackThread> thread;
+ {
+ Mutex::Autolock _l(mLock);
+ thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("closeOutput() %d", output);
+
+ if (thread->type() == PlaybackThread::MIXER) {
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->type() == PlaybackThread::DUPLICATING) {
+ DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
+ dupThread->removeOutputTrack((MixerThread *)thread.get());
+ }
+ }
+ }
+ void *param2 = 0;
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
+ mPlaybackThreads.removeItem(output);
+ }
+ thread->exit();
+
+ if (thread->type() != PlaybackThread::DUPLICATING) {
+ mAudioHardware->closeOutputStream(thread->getOutput());
+ }
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::suspendOutput(int output)
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("suspendOutput() %d", output);
+ thread->suspend();
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::restoreOutput(int output)
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("restoreOutput() %d", output);
+
+ thread->restore();
+
+ return NO_ERROR;
+}
+
+int AudioFlinger::openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ status_t status;
+ RecordThread *thread = NULL;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+ uint32_t reqSamplingRate = samplingRate;
+ uint32_t reqFormat = format;
+ uint32_t reqChannels = channels;
+
+ if (pDevices == NULL || *pDevices == 0) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+
+ AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status,
+ (AudioSystem::audio_in_acoustics)acoustics);
+ LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
+ input,
+ samplingRate,
+ format,
+ channels,
+ acoustics,
+ status);
+
+ // If the input could not be opened with the requested parameters and we can handle the conversion internally,
+ // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
+ // or stereo to mono conversions on 16 bit PCM inputs.
+ if (input == 0 && status == BAD_VALUE &&
+ reqFormat == format && format == AudioSystem::PCM_16_BIT &&
+ (samplingRate <= 2 * reqSamplingRate) &&
+ (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) {
+ LOGV("openInput() reopening with proposed sampling rate and channels");
+ input = mAudioHardware->openInputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status,
+ (AudioSystem::audio_in_acoustics)acoustics);
+ }
+
+ if (input != 0) {
+ // Start record thread
+ thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId);
+ mRecordThreads.add(mNextThreadId, thread);
+ LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
+ if (pSamplingRate) *pSamplingRate = reqSamplingRate;
+ if (pFormat) *pFormat = format;
+ if (pChannels) *pChannels = reqChannels;
+
+ input->standby();
+
+ return mNextThreadId;
+ }
+
+ return 0;
+}
+
+status_t AudioFlinger::closeInput(int input)
+{
+ // keep strong reference on the record thread so that
+ // it is not destroyed while exit() is executed
+ sp <RecordThread> thread;
+ {
+ Mutex::Autolock _l(mLock);
+ thread = checkRecordThread_l(input);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("closeInput() %d", input);
+ void *param2 = 0;
+ audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
+ mRecordThreads.removeItem(input);
+ }
+ thread->exit();
+
+ mAudioHardware->closeInputStream(thread->getInput());
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::setStreamOutput(uint32_t stream, int output)
+{
+ Mutex::Autolock _l(mLock);
+ MixerThread *dstThread = checkMixerThread_l(output);
+ if (dstThread == NULL) {
+ LOGW("setStreamOutput() bad output id %d", output);
+ return BAD_VALUE;
+ }
+
+ LOGV("setStreamOutput() stream %d to output %d", stream, output);
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (thread != dstThread &&
+ thread->type() != PlaybackThread::DIRECT) {
+ MixerThread *srcThread = (MixerThread *)thread;
+ SortedVector < sp<MixerThread::Track> > tracks;
+ SortedVector < wp<MixerThread::Track> > activeTracks;
+ srcThread->getTracks(tracks, activeTracks, stream);
+ if (tracks.size()) {
+ dstThread->putTracks(tracks, activeTracks);
+ }
+ }
+ }
+
+ dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream);
+
+ return NO_ERROR;
+}
+
+// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
+{
+ PlaybackThread *thread = NULL;
+ if (mPlaybackThreads.indexOfKey(output) >= 0) {
+ thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get();
+ }
+ return thread;
+}
+
+// checkMixerThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(int output) const
+{
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread != NULL) {
+ if (thread->type() == PlaybackThread::DIRECT) {
+ thread = NULL;
+ }
+ }
+ return (MixerThread *)thread;
+}
+
+// checkRecordThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const
+{
+ RecordThread *thread = NULL;
+ if (mRecordThreads.indexOfKey(input) >= 0) {
+ thread = (RecordThread *)mRecordThreads.valueFor(input).get();
+ }
+ return thread;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t AudioFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioFlinger::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+
+void AudioFlinger::instantiate() {
+ defaultServiceManager()->addService(
+ String16("media.audio_flinger"), new AudioFlinger());
+}
+
+}; // namespace android
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
new file mode 100644
index 0000000..739ec33
--- /dev/null
+++ b/services/audioflinger/AudioFlinger.h
@@ -0,0 +1,807 @@
+/* //device/include/server/AudioFlinger/AudioFlinger.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_FLINGER_H
+#define ANDROID_AUDIO_FLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include <media/IAudioFlinger.h>
+#include <media/IAudioFlingerClient.h>
+#include <media/IAudioTrack.h>
+#include <media/IAudioRecord.h>
+#include <media/AudioTrack.h>
+
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <binder/MemoryDealer.h>
+#include <utils/SortedVector.h>
+#include <utils/Vector.h>
+
+#include <hardware_legacy/AudioHardwareInterface.h>
+
+#include "AudioBufferProvider.h"
+
+namespace android {
+
+class audio_track_cblk_t;
+class AudioMixer;
+class AudioBuffer;
+class AudioResampler;
+
+
+// ----------------------------------------------------------------------------
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+
+// ----------------------------------------------------------------------------
+
+static const nsecs_t kStandbyTimeInNsecs = seconds(3);
+
+class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient
+{
+public:
+ static void instantiate();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ // IAudioFlinger interface
+ virtual sp<IAudioTrack> createTrack(
+ pid_t pid,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ int output,
+ status_t *status);
+
+ virtual uint32_t sampleRate(int output) const;
+ virtual int channelCount(int output) const;
+ virtual int format(int output) const;
+ virtual size_t frameCount(int output) const;
+ virtual uint32_t latency(int output) const;
+
+ virtual status_t setMasterVolume(float value);
+ virtual status_t setMasterMute(bool muted);
+
+ virtual float masterVolume() const;
+ virtual bool masterMute() const;
+
+ virtual status_t setStreamVolume(int stream, float value, int output);
+ virtual status_t setStreamMute(int stream, bool muted);
+
+ virtual float streamVolume(int stream, int output) const;
+ virtual bool streamMute(int stream) const;
+
+ virtual status_t setMode(int mode);
+
+ virtual status_t setMicMute(bool state);
+ virtual bool getMicMute() const;
+
+ virtual bool isStreamActive(int stream) const;
+
+ virtual status_t setParameters(int ioHandle, const String8& keyValuePairs);
+ virtual String8 getParameters(int ioHandle, const String8& keys);
+
+ virtual void registerClient(const sp<IAudioFlingerClient>& client);
+
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
+ virtual unsigned int getInputFramesLost(int ioHandle);
+
+ virtual int openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags);
+
+ virtual int openDuplicateOutput(int output1, int output2);
+
+ virtual status_t closeOutput(int output);
+
+ virtual status_t suspendOutput(int output);
+
+ virtual status_t restoreOutput(int output);
+
+ virtual int openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics);
+
+ virtual status_t closeInput(int input);
+
+ virtual status_t setStreamOutput(uint32_t stream, int output);
+
+ virtual status_t setVoiceVolume(float volume);
+
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ enum hardware_call_state {
+ AUDIO_HW_IDLE = 0,
+ AUDIO_HW_INIT,
+ AUDIO_HW_OUTPUT_OPEN,
+ AUDIO_HW_OUTPUT_CLOSE,
+ AUDIO_HW_INPUT_OPEN,
+ AUDIO_HW_INPUT_CLOSE,
+ AUDIO_HW_STANDBY,
+ AUDIO_HW_SET_MASTER_VOLUME,
+ AUDIO_HW_GET_ROUTING,
+ AUDIO_HW_SET_ROUTING,
+ AUDIO_HW_GET_MODE,
+ AUDIO_HW_SET_MODE,
+ AUDIO_HW_GET_MIC_MUTE,
+ AUDIO_HW_SET_MIC_MUTE,
+ AUDIO_SET_VOICE_VOLUME,
+ AUDIO_SET_PARAMETER,
+ };
+
+ // record interface
+ virtual sp<IAudioRecord> openRecord(
+ pid_t pid,
+ int input,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ status_t *status);
+
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags);
+
+private:
+ AudioFlinger();
+ virtual ~AudioFlinger();
+
+
+ // Internal dump utilites.
+ status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
+ status_t dumpClients(int fd, const Vector<String16>& args);
+ status_t dumpInternals(int fd, const Vector<String16>& args);
+
+ // --- Client ---
+ class Client : public RefBase {
+ public:
+ Client(const sp<AudioFlinger>& audioFlinger, pid_t pid);
+ virtual ~Client();
+ const sp<MemoryDealer>& heap() const;
+ pid_t pid() const { return mPid; }
+ sp<AudioFlinger> audioFlinger() { return mAudioFlinger; }
+
+ private:
+ Client(const Client&);
+ Client& operator = (const Client&);
+ sp<AudioFlinger> mAudioFlinger;
+ sp<MemoryDealer> mMemoryDealer;
+ pid_t mPid;
+ };
+
+
+ class TrackHandle;
+ class RecordHandle;
+ class RecordThread;
+ class PlaybackThread;
+ class MixerThread;
+ class DirectOutputThread;
+ class DuplicatingThread;
+ class Track;
+ class RecordTrack;
+
+ class ThreadBase : public Thread {
+ public:
+ ThreadBase (const sp<AudioFlinger>& audioFlinger, int id);
+ virtual ~ThreadBase();
+
+ status_t dumpBase(int fd, const Vector<String16>& args);
+
+ // base for record and playback
+ class TrackBase : public AudioBufferProvider, public RefBase {
+
+ public:
+ enum track_state {
+ IDLE,
+ TERMINATED,
+ STOPPED,
+ RESUMING,
+ ACTIVE,
+ PAUSING,
+ PAUSED
+ };
+
+ enum track_flags {
+ STEPSERVER_FAILED = 0x01, // StepServer could not acquire cblk->lock mutex
+ SYSTEM_FLAGS_MASK = 0x0000ffffUL,
+ // The upper 16 bits are used for track-specific flags.
+ };
+
+ TrackBase(const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer);
+ ~TrackBase();
+
+ virtual status_t start() = 0;
+ virtual void stop() = 0;
+ sp<IMemory> getCblk() const;
+ audio_track_cblk_t* cblk() const { return mCblk; }
+
+ protected:
+ friend class ThreadBase;
+ friend class RecordHandle;
+ friend class PlaybackThread;
+ friend class RecordThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+
+ TrackBase(const TrackBase&);
+ TrackBase& operator = (const TrackBase&);
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+
+ int format() const {
+ return mFormat;
+ }
+
+ int channelCount() const ;
+
+ int sampleRate() const;
+
+ void* getBuffer(uint32_t offset, uint32_t frames) const;
+
+ bool isStopped() const {
+ return mState == STOPPED;
+ }
+
+ bool isTerminated() const {
+ return mState == TERMINATED;
+ }
+
+ bool step();
+ void reset();
+
+ wp<ThreadBase> mThread;
+ sp<Client> mClient;
+ sp<IMemory> mCblkMemory;
+ audio_track_cblk_t* mCblk;
+ void* mBuffer;
+ void* mBufferEnd;
+ uint32_t mFrameCount;
+ // we don't really need a lock for these
+ int mState;
+ int mClientTid;
+ uint8_t mFormat;
+ uint32_t mFlags;
+ };
+
+ class ConfigEvent {
+ public:
+ ConfigEvent() : mEvent(0), mParam(0) {}
+
+ int mEvent;
+ int mParam;
+ };
+
+ uint32_t sampleRate() const;
+ int channelCount() const;
+ int format() const;
+ size_t frameCount() const;
+ void wakeUp() { mWaitWorkCV.broadcast(); }
+ void exit();
+ virtual bool checkForNewParameters_l() = 0;
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys) = 0;
+ virtual void audioConfigChanged(int event, int param = 0) = 0;
+ void sendConfigEvent(int event, int param = 0);
+ void sendConfigEvent_l(int event, int param = 0);
+ void processConfigEvents();
+ int id() const { return mId;}
+ bool standby() { return mStandby; }
+
+ mutable Mutex mLock;
+
+ protected:
+
+ friend class Track;
+ friend class TrackBase;
+ friend class PlaybackThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+ friend class DuplicatingThread;
+ friend class RecordThread;
+ friend class RecordTrack;
+
+ Condition mWaitWorkCV;
+ sp<AudioFlinger> mAudioFlinger;
+ uint32_t mSampleRate;
+ size_t mFrameCount;
+ int mChannelCount;
+ int mFormat;
+ uint32_t mFrameSize;
+ Condition mParamCond;
+ Vector<String8> mNewParameters;
+ status_t mParamStatus;
+ Vector<ConfigEvent *> mConfigEvents;
+ bool mStandby;
+ int mId;
+ bool mExiting;
+ };
+
+ // --- PlaybackThread ---
+ class PlaybackThread : public ThreadBase {
+ public:
+
+ enum type {
+ MIXER,
+ DIRECT,
+ DUPLICATING
+ };
+
+ enum mixer_state {
+ MIXER_IDLE,
+ MIXER_TRACKS_ENABLED,
+ MIXER_TRACKS_READY
+ };
+
+ // playback track
+ class Track : public TrackBase {
+ public:
+ Track( const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ const sp<IMemory>& sharedBuffer);
+ ~Track();
+
+ void dump(char* buffer, size_t size);
+ virtual status_t start();
+ virtual void stop();
+ void pause();
+
+ void flush();
+ void destroy();
+ void mute(bool);
+ void setVolume(float left, float right);
+ int name() const {
+ return mName;
+ }
+
+ int type() const {
+ return mStreamType;
+ }
+
+
+ protected:
+ friend class ThreadBase;
+ friend class AudioFlinger;
+ friend class TrackHandle;
+ friend class PlaybackThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+
+ Track(const Track&);
+ Track& operator = (const Track&);
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+ bool isMuted() { return mMute; }
+ bool isPausing() const {
+ return mState == PAUSING;
+ }
+ bool isPaused() const {
+ return mState == PAUSED;
+ }
+ bool isReady() const;
+ void setPaused() { mState = PAUSED; }
+ void reset();
+
+ bool isOutputTrack() const {
+ return (mStreamType == AudioSystem::NUM_STREAM_TYPES);
+ }
+
+ // we don't really need a lock for these
+ float mVolume[2];
+ volatile bool mMute;
+ // FILLED state is used for suppressing volume ramp at begin of playing
+ enum {FS_FILLING, FS_FILLED, FS_ACTIVE};
+ mutable uint8_t mFillingUpStatus;
+ int8_t mRetryCount;
+ sp<IMemory> mSharedBuffer;
+ bool mResetDone;
+ int mStreamType;
+ int mName;
+ }; // end of Track
+
+
+ // playback track
+ class OutputTrack : public Track {
+ public:
+
+ class Buffer: public AudioBufferProvider::Buffer {
+ public:
+ int16_t *mBuffer;
+ };
+
+ OutputTrack( const wp<ThreadBase>& thread,
+ DuplicatingThread *sourceThread,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount);
+ ~OutputTrack();
+
+ virtual status_t start();
+ virtual void stop();
+ bool write(int16_t* data, uint32_t frames);
+ bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
+ bool isActive() { return mActive; }
+ wp<ThreadBase>& thread() { return mThread; }
+
+ private:
+
+ status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs);
+ void clearBufferQueue();
+
+ // Maximum number of pending buffers allocated by OutputTrack::write()
+ static const uint8_t kMaxOverFlowBuffers = 10;
+
+ Vector < Buffer* > mBufferQueue;
+ AudioBufferProvider::Buffer mOutBuffer;
+ bool mActive;
+ DuplicatingThread* mSourceThread;
+ }; // end of OutputTrack
+
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ virtual ~PlaybackThread();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ // Thread virtuals
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+
+ virtual uint32_t latency() const;
+
+ virtual status_t setMasterVolume(float value);
+ virtual status_t setMasterMute(bool muted);
+
+ virtual float masterVolume() const;
+ virtual bool masterMute() const;
+
+ virtual status_t setStreamVolume(int stream, float value);
+ virtual status_t setStreamMute(int stream, bool muted);
+
+ virtual float streamVolume(int stream) const;
+ virtual bool streamMute(int stream) const;
+
+ bool isStreamActive(int stream) const;
+
+ sp<Track> createTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ const sp<IMemory>& sharedBuffer,
+ status_t *status);
+
+ AudioStreamOut* getOutput() { return mOutput; }
+
+ virtual int type() const { return mType; }
+ void suspend() { mSuspended++; }
+ void restore() { if (mSuspended) mSuspended--; }
+ bool isSuspended() { return (mSuspended != 0); }
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged(int event, int param = 0);
+ virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+
+ struct stream_type_t {
+ stream_type_t()
+ : volume(1.0f),
+ mute(false)
+ {
+ }
+ float volume;
+ bool mute;
+ };
+
+ protected:
+ int mType;
+ int16_t* mMixBuffer;
+ int mSuspended;
+ int mBytesWritten;
+ bool mMasterMute;
+ SortedVector< wp<Track> > mActiveTracks;
+
+ virtual int getTrackName_l() = 0;
+ virtual void deleteTrackName_l(int name) = 0;
+ virtual uint32_t activeSleepTimeUs() = 0;
+ virtual uint32_t idleSleepTimeUs() = 0;
+
+ private:
+
+ friend class AudioFlinger;
+ friend class OutputTrack;
+ friend class Track;
+ friend class TrackBase;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+ friend class DuplicatingThread;
+
+ PlaybackThread(const Client&);
+ PlaybackThread& operator = (const PlaybackThread&);
+
+ status_t addTrack_l(const sp<Track>& track);
+ void destroyTrack_l(const sp<Track>& track);
+
+ void readOutputParameters();
+
+ virtual status_t dumpInternals(int fd, const Vector<String16>& args);
+ status_t dumpTracks(int fd, const Vector<String16>& args);
+
+ SortedVector< sp<Track> > mTracks;
+ // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
+ stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1];
+ AudioStreamOut* mOutput;
+ float mMasterVolume;
+ nsecs_t mLastWriteTime;
+ int mNumWrites;
+ int mNumDelayedWrites;
+ bool mInWrite;
+ };
+
+ class MixerThread : public PlaybackThread {
+ public:
+ MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ virtual ~MixerThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ void getTracks(SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks,
+ int streamType);
+ void putTracks(SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks);
+ virtual bool checkForNewParameters_l();
+ virtual status_t dumpInternals(int fd, const Vector<String16>& args);
+
+ protected:
+ uint32_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove);
+ virtual int getTrackName_l();
+ virtual void deleteTrackName_l(int name);
+ virtual uint32_t activeSleepTimeUs();
+ virtual uint32_t idleSleepTimeUs();
+
+ AudioMixer* mAudioMixer;
+ };
+
+ class DirectOutputThread : public PlaybackThread {
+ public:
+
+ DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
+ ~DirectOutputThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ virtual bool checkForNewParameters_l();
+
+ protected:
+ virtual int getTrackName_l();
+ virtual void deleteTrackName_l(int name);
+ virtual uint32_t activeSleepTimeUs();
+ virtual uint32_t idleSleepTimeUs();
+
+ private:
+ float mLeftVolume;
+ float mRightVolume;
+ };
+
+ class DuplicatingThread : public MixerThread {
+ public:
+ DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread, int id);
+ ~DuplicatingThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+ void addOutputTrack(MixerThread* thread);
+ void removeOutputTrack(MixerThread* thread);
+ uint32_t waitTimeMs() { return mWaitTimeMs; }
+ protected:
+ virtual uint32_t activeSleepTimeUs();
+
+ private:
+ bool outputsReady(SortedVector< sp<OutputTrack> > &outputTracks);
+ void updateWaitTime();
+
+ SortedVector < sp<OutputTrack> > mOutputTracks;
+ uint32_t mWaitTimeMs;
+ };
+
+ PlaybackThread *checkPlaybackThread_l(int output) const;
+ MixerThread *checkMixerThread_l(int output) const;
+ RecordThread *checkRecordThread_l(int input) const;
+ float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
+ void audioConfigChanged_l(int event, int ioHandle, void *param2);
+
+ friend class AudioBuffer;
+
+ class TrackHandle : public android::BnAudioTrack {
+ public:
+ TrackHandle(const sp<PlaybackThread::Track>& track);
+ virtual ~TrackHandle();
+ virtual status_t start();
+ virtual void stop();
+ virtual void flush();
+ virtual void mute(bool);
+ virtual void pause();
+ virtual void setVolume(float left, float right);
+ virtual sp<IMemory> getCblk() const;
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ private:
+ sp<PlaybackThread::Track> mTrack;
+ };
+
+ friend class Client;
+ friend class PlaybackThread::Track;
+
+
+ void removeClient_l(pid_t pid);
+
+
+ // record thread
+ class RecordThread : public ThreadBase, public AudioBufferProvider
+ {
+ public:
+
+ // record track
+ class RecordTrack : public TrackBase {
+ public:
+ RecordTrack(const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags);
+ ~RecordTrack();
+
+ virtual status_t start();
+ virtual void stop();
+
+ bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
+ bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
+
+ void dump(char* buffer, size_t size);
+ private:
+ friend class AudioFlinger;
+ friend class RecordThread;
+
+ RecordTrack(const RecordTrack&);
+ RecordTrack& operator = (const RecordTrack&);
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+
+ bool mOverflow;
+ };
+
+
+ RecordThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamIn *input,
+ uint32_t sampleRate,
+ uint32_t channels,
+ int id);
+ ~RecordThread();
+
+ virtual bool threadLoop();
+ virtual status_t readyToRun() { return NO_ERROR; }
+ virtual void onFirstRef();
+
+ status_t start(RecordTrack* recordTrack);
+ void stop(RecordTrack* recordTrack);
+ status_t dump(int fd, const Vector<String16>& args);
+ AudioStreamIn* getInput() { return mInput; }
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+ virtual bool checkForNewParameters_l();
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged(int event, int param = 0);
+ void readInputParameters();
+ virtual unsigned int getInputFramesLost();
+
+ private:
+ RecordThread();
+ AudioStreamIn *mInput;
+ sp<RecordTrack> mActiveTrack;
+ Condition mStartStopCond;
+ AudioResampler *mResampler;
+ int32_t *mRsmpOutBuffer;
+ int16_t *mRsmpInBuffer;
+ size_t mRsmpInIndex;
+ size_t mInputBytes;
+ int mReqChannelCount;
+ uint32_t mReqSampleRate;
+ ssize_t mBytesRead;
+ };
+
+ class RecordHandle : public android::BnAudioRecord {
+ public:
+ RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
+ virtual ~RecordHandle();
+ virtual status_t start();
+ virtual void stop();
+ virtual sp<IMemory> getCblk() const;
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ private:
+ sp<RecordThread::RecordTrack> mRecordTrack;
+ };
+
+ friend class RecordThread;
+ friend class PlaybackThread;
+
+
+ mutable Mutex mLock;
+
+ DefaultKeyedVector< pid_t, wp<Client> > mClients;
+
+ mutable Mutex mHardwareLock;
+ AudioHardwareInterface* mAudioHardware;
+ mutable int mHardwareStatus;
+
+
+ DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
+ PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
+ float mMasterVolume;
+ bool mMasterMute;
+
+ DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads;
+
+ SortedVector< sp<IBinder> > mNotificationClients;
+ int mNextThreadId;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_FLINGER_H
diff --git a/services/audioflinger/AudioHardwareGeneric.cpp b/services/audioflinger/AudioHardwareGeneric.cpp
new file mode 100644
index 0000000..d63c031
--- /dev/null
+++ b/services/audioflinger/AudioHardwareGeneric.cpp
@@ -0,0 +1,411 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#define LOG_TAG "AudioHardware"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "AudioHardwareGeneric.h"
+#include <media/AudioRecord.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static char const * const kAudioDeviceName = "/dev/eac";
+
+// ----------------------------------------------------------------------------
+
+AudioHardwareGeneric::AudioHardwareGeneric()
+ : mOutput(0), mInput(0), mFd(-1), mMicMute(false)
+{
+ mFd = ::open(kAudioDeviceName, O_RDWR);
+}
+
+AudioHardwareGeneric::~AudioHardwareGeneric()
+{
+ if (mFd >= 0) ::close(mFd);
+ closeOutputStream((AudioStreamOut *)mOutput);
+ closeInputStream((AudioStreamIn *)mInput);
+}
+
+status_t AudioHardwareGeneric::initCheck()
+{
+ if (mFd >= 0) {
+ if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR)
+ return NO_ERROR;
+ }
+ return NO_INIT;
+}
+
+AudioStreamOut* AudioHardwareGeneric::openOutputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+ AutoMutex lock(mLock);
+
+ // only one output stream allowed
+ if (mOutput) {
+ if (status) {
+ *status = INVALID_OPERATION;
+ }
+ return 0;
+ }
+
+ // create new output stream
+ AudioStreamOutGeneric* out = new AudioStreamOutGeneric();
+ status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR) {
+ mOutput = out;
+ } else {
+ delete out;
+ }
+ return mOutput;
+}
+
+void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) {
+ if (mOutput && out == mOutput) {
+ delete mOutput;
+ mOutput = 0;
+ }
+}
+
+AudioStreamIn* AudioHardwareGeneric::openInputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
+ status_t *status, AudioSystem::audio_in_acoustics acoustics)
+{
+ // check for valid input source
+ if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+ return 0;
+ }
+
+ AutoMutex lock(mLock);
+
+ // only one input stream allowed
+ if (mInput) {
+ if (status) {
+ *status = INVALID_OPERATION;
+ }
+ return 0;
+ }
+
+ // create new output stream
+ AudioStreamInGeneric* in = new AudioStreamInGeneric();
+ status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR) {
+ mInput = in;
+ } else {
+ delete in;
+ }
+ return mInput;
+}
+
+void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) {
+ if (mInput && in == mInput) {
+ delete mInput;
+ mInput = 0;
+ }
+}
+
+status_t AudioHardwareGeneric::setVoiceVolume(float v)
+{
+ // Implement: set voice volume
+ return NO_ERROR;
+}
+
+status_t AudioHardwareGeneric::setMasterVolume(float v)
+{
+ // Implement: set master volume
+ // return error - software mixer will handle it
+ return INVALID_OPERATION;
+}
+
+status_t AudioHardwareGeneric::setMicMute(bool state)
+{
+ mMicMute = state;
+ return NO_ERROR;
+}
+
+status_t AudioHardwareGeneric::getMicMute(bool* state)
+{
+ *state = mMicMute;
+ return NO_ERROR;
+}
+
+status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ result.append("AudioHardwareGeneric::dumpInternals\n");
+ snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false");
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ if (mInput) {
+ mInput->dump(fd, args);
+ }
+ if (mOutput) {
+ mOutput->dump(fd, args);
+ }
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t AudioStreamOutGeneric::set(
+ AudioHardwareGeneric *hw,
+ int fd,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate)
+{
+ int lFormat = pFormat ? *pFormat : 0;
+ uint32_t lChannels = pChannels ? *pChannels : 0;
+ uint32_t lRate = pRate ? *pRate : 0;
+
+ // fix up defaults
+ if (lFormat == 0) lFormat = format();
+ if (lChannels == 0) lChannels = channels();
+ if (lRate == 0) lRate = sampleRate();
+
+ // check values
+ if ((lFormat != format()) ||
+ (lChannels != channels()) ||
+ (lRate != sampleRate())) {
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
+ return BAD_VALUE;
+ }
+
+ if (pFormat) *pFormat = lFormat;
+ if (pChannels) *pChannels = lChannels;
+ if (pRate) *pRate = lRate;
+
+ mAudioHardware = hw;
+ mFd = fd;
+ mDevice = devices;
+ return NO_ERROR;
+}
+
+AudioStreamOutGeneric::~AudioStreamOutGeneric()
+{
+}
+
+ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
+{
+ Mutex::Autolock _l(mLock);
+ return ssize_t(::write(mFd, buffer, bytes));
+}
+
+status_t AudioStreamOutGeneric::standby()
+{
+ // Implement: audio hardware to standby mode
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tformat: %d\n", format());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDevice = device;
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 AudioStreamOutGeneric::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+status_t AudioStreamOutGeneric::getRenderPosition(uint32_t *dspFrames)
+{
+ return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
+
+// record functions
+status_t AudioStreamInGeneric::set(
+ AudioHardwareGeneric *hw,
+ int fd,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE;
+ LOGV("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate);
+ // check values
+ if ((*pFormat != format()) ||
+ (*pChannels != channels()) ||
+ (*pRate != sampleRate())) {
+ LOGE("Error opening input channel");
+ *pFormat = format();
+ *pChannels = channels();
+ *pRate = sampleRate();
+ return BAD_VALUE;
+ }
+
+ mAudioHardware = hw;
+ mFd = fd;
+ mDevice = devices;
+ return NO_ERROR;
+}
+
+AudioStreamInGeneric::~AudioStreamInGeneric()
+{
+}
+
+ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes)
+{
+ AutoMutex lock(mLock);
+ if (mFd < 0) {
+ LOGE("Attempt to read from unopened device");
+ return NO_INIT;
+ }
+ return ::read(mFd, buffer, bytes);
+}
+
+status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tformat: %d\n", format());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDevice = device;
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 AudioStreamInGeneric::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/audioflinger/AudioHardwareGeneric.h b/services/audioflinger/AudioHardwareGeneric.h
new file mode 100644
index 0000000..aa4e78d
--- /dev/null
+++ b/services/audioflinger/AudioHardwareGeneric.h
@@ -0,0 +1,151 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_HARDWARE_GENERIC_H
+#define ANDROID_AUDIO_HARDWARE_GENERIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class AudioHardwareGeneric;
+
+class AudioStreamOutGeneric : public AudioStreamOut {
+public:
+ AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {}
+ virtual ~AudioStreamOutGeneric();
+
+ virtual status_t set(
+ AudioHardwareGeneric *hw,
+ int mFd,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate);
+
+ virtual uint32_t sampleRate() const { return 44100; }
+ virtual size_t bufferSize() const { return 4096; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
+ virtual int format() const { return AudioSystem::PCM_16_BIT; }
+ virtual uint32_t latency() const { return 20; }
+ virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ virtual status_t standby();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+private:
+ AudioHardwareGeneric *mAudioHardware;
+ Mutex mLock;
+ int mFd;
+ uint32_t mDevice;
+};
+
+class AudioStreamInGeneric : public AudioStreamIn {
+public:
+ AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {}
+ virtual ~AudioStreamInGeneric();
+
+ virtual status_t set(
+ AudioHardwareGeneric *hw,
+ int mFd,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate,
+ AudioSystem::audio_in_acoustics acoustics);
+
+ virtual uint32_t sampleRate() const { return 8000; }
+ virtual size_t bufferSize() const { return 320; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
+ virtual int format() const { return AudioSystem::PCM_16_BIT; }
+ virtual status_t setGain(float gain) { return INVALID_OPERATION; }
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t standby() { return NO_ERROR; }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual unsigned int getInputFramesLost() const { return 0; }
+
+private:
+ AudioHardwareGeneric *mAudioHardware;
+ Mutex mLock;
+ int mFd;
+ uint32_t mDevice;
+};
+
+
+class AudioHardwareGeneric : public AudioHardwareBase
+{
+public:
+ AudioHardwareGeneric();
+ virtual ~AudioHardwareGeneric();
+ virtual status_t initCheck();
+ virtual status_t setVoiceVolume(float volume);
+ virtual status_t setMasterVolume(float volume);
+
+ // mic mute
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool* state);
+
+ // create I/O streams
+ virtual AudioStreamOut* openOutputStream(
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
+ status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
+ virtual AudioStreamIn* openInputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+
+ void closeOutputStream(AudioStreamOutGeneric* out);
+ void closeInputStream(AudioStreamInGeneric* in);
+protected:
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ status_t dumpInternals(int fd, const Vector<String16>& args);
+
+ Mutex mLock;
+ AudioStreamOutGeneric *mOutput;
+ AudioStreamInGeneric *mInput;
+ int mFd;
+ bool mMicMute;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H
diff --git a/services/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp
new file mode 100644
index 0000000..9a4a7f9
--- /dev/null
+++ b/services/audioflinger/AudioHardwareInterface.cpp
@@ -0,0 +1,182 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <cutils/properties.h>
+#include <string.h>
+#include <unistd.h>
+//#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioHardwareInterface"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include "AudioHardwareStub.h"
+#include "AudioHardwareGeneric.h"
+#ifdef WITH_A2DP
+#include "A2dpAudioInterface.h"
+#endif
+
+#ifdef ENABLE_AUDIO_DUMP
+#include "AudioDumpInterface.h"
+#endif
+
+
+// change to 1 to log routing calls
+#define LOG_ROUTING_CALLS 1
+
+namespace android {
+
+#if LOG_ROUTING_CALLS
+static const char* routingModeStrings[] =
+{
+ "OUT OF RANGE",
+ "INVALID",
+ "CURRENT",
+ "NORMAL",
+ "RINGTONE",
+ "IN_CALL"
+};
+
+static const char* routeNone = "NONE";
+
+static const char* displayMode(int mode)
+{
+ if ((mode < -2) || (mode > 2))
+ return routingModeStrings[0];
+ return routingModeStrings[mode+3];
+}
+#endif
+
+// ----------------------------------------------------------------------------
+
+AudioHardwareInterface* AudioHardwareInterface::create()
+{
+ /*
+ * FIXME: This code needs to instantiate the correct audio device
+ * interface. For now - we use compile-time switches.
+ */
+ AudioHardwareInterface* hw = 0;
+ char value[PROPERTY_VALUE_MAX];
+
+#ifdef GENERIC_AUDIO
+ hw = new AudioHardwareGeneric();
+#else
+ // if running in emulation - use the emulator driver
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ LOGD("Running in emulation - using generic audio driver");
+ hw = new AudioHardwareGeneric();
+ }
+ else {
+ LOGV("Creating Vendor Specific AudioHardware");
+ hw = createAudioHardware();
+ }
+#endif
+ if (hw->initCheck() != NO_ERROR) {
+ LOGW("Using stubbed audio hardware. No sound will be produced.");
+ delete hw;
+ hw = new AudioHardwareStub();
+ }
+
+#ifdef WITH_A2DP
+ hw = new A2dpAudioInterface(hw);
+#endif
+
+#ifdef ENABLE_AUDIO_DUMP
+ // This code adds a record of buffers in a file to write calls made by AudioFlinger.
+ // It replaces the current AudioHardwareInterface object by an intermediate one which
+ // will record buffers in a file (after sending them to hardware) for testing purpose.
+ // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP.
+ // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file.
+ LOGV("opening PCM dump interface");
+ hw = new AudioDumpInterface(hw); // replace interface
+#endif
+ return hw;
+}
+
+AudioStreamOut::~AudioStreamOut()
+{
+}
+
+AudioStreamIn::~AudioStreamIn() {}
+
+AudioHardwareBase::AudioHardwareBase()
+{
+ mMode = 0;
+}
+
+status_t AudioHardwareBase::setMode(int mode)
+{
+#if LOG_ROUTING_CALLS
+ LOGD("setMode(%s)", displayMode(mode));
+#endif
+ if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
+ return BAD_VALUE;
+ if (mMode == mode)
+ return ALREADY_EXISTS;
+ mMode = mode;
+ return NO_ERROR;
+}
+
+// default implementation
+status_t AudioHardwareBase::setParameters(const String8& keyValuePairs)
+{
+ return NO_ERROR;
+}
+
+// default implementation
+String8 AudioHardwareBase::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+// default implementation
+size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+ if (sampleRate != 8000) {
+ LOGW("getInputBufferSize bad sampling rate: %d", sampleRate);
+ return 0;
+ }
+ if (format != AudioSystem::PCM_16_BIT) {
+ LOGW("getInputBufferSize bad format: %d", format);
+ return 0;
+ }
+ if (channelCount != 1) {
+ LOGW("getInputBufferSize bad channel count: %d", channelCount);
+ return 0;
+ }
+
+ return 320;
+}
+
+status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmMode: %d\n", mMode);
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ dump(fd, args); // Dump the state of the concrete child.
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.cpp b/services/audioflinger/AudioHardwareStub.cpp
new file mode 100644
index 0000000..d481150
--- /dev/null
+++ b/services/audioflinger/AudioHardwareStub.cpp
@@ -0,0 +1,209 @@
+/* //device/servers/AudioFlinger/AudioHardwareStub.cpp
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+#include "AudioHardwareStub.h"
+#include <media/AudioRecord.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+AudioHardwareStub::AudioHardwareStub() : mMicMute(false)
+{
+}
+
+AudioHardwareStub::~AudioHardwareStub()
+{
+}
+
+status_t AudioHardwareStub::initCheck()
+{
+ return NO_ERROR;
+}
+
+AudioStreamOut* AudioHardwareStub::openOutputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+ AudioStreamOutStub* out = new AudioStreamOutStub();
+ status_t lStatus = out->set(format, channels, sampleRate);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR)
+ return out;
+ delete out;
+ return 0;
+}
+
+void AudioHardwareStub::closeOutputStream(AudioStreamOut* out)
+{
+ delete out;
+}
+
+AudioStreamIn* AudioHardwareStub::openInputStream(
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
+ status_t *status, AudioSystem::audio_in_acoustics acoustics)
+{
+ // check for valid input source
+ if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+ return 0;
+ }
+
+ AudioStreamInStub* in = new AudioStreamInStub();
+ status_t lStatus = in->set(format, channels, sampleRate, acoustics);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR)
+ return in;
+ delete in;
+ return 0;
+}
+
+void AudioHardwareStub::closeInputStream(AudioStreamIn* in)
+{
+ delete in;
+}
+
+status_t AudioHardwareStub::setVoiceVolume(float volume)
+{
+ return NO_ERROR;
+}
+
+status_t AudioHardwareStub::setMasterVolume(float volume)
+{
+ return NO_ERROR;
+}
+
+status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ result.append("AudioHardwareStub::dumpInternals\n");
+ snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate)
+{
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
+
+ return NO_ERROR;
+}
+
+ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes)
+{
+ // fake timing for audio output
+ usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
+ return bytes;
+}
+
+status_t AudioStreamOutStub::standby()
+{
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n");
+ snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+ snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+ snprintf(buffer, SIZE, "\tformat: %d\n", format());
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+String8 AudioStreamOutStub::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames)
+{
+ return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
+
+status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ return NO_ERROR;
+}
+
+ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes)
+{
+ // fake timing for audio input
+ usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
+ memset(buffer, 0, bytes);
+ return bytes;
+}
+
+status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "AudioStreamInStub::dump\n");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tformat: %d\n", format());
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+String8 AudioStreamInStub::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/audioflinger/AudioHardwareStub.h b/services/audioflinger/AudioHardwareStub.h
new file mode 100644
index 0000000..06a29de
--- /dev/null
+++ b/services/audioflinger/AudioHardwareStub.h
@@ -0,0 +1,106 @@
+/* //device/servers/AudioFlinger/AudioHardwareStub.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_HARDWARE_STUB_H
+#define ANDROID_AUDIO_HARDWARE_STUB_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class AudioStreamOutStub : public AudioStreamOut {
+public:
+ virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate);
+ virtual uint32_t sampleRate() const { return 44100; }
+ virtual size_t bufferSize() const { return 4096; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
+ virtual int format() const { return AudioSystem::PCM_16_BIT; }
+ virtual uint32_t latency() const { return 0; }
+ virtual status_t setVolume(float left, float right) { return NO_ERROR; }
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ virtual status_t standby();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+};
+
+class AudioStreamInStub : public AudioStreamIn {
+public:
+ virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics);
+ virtual uint32_t sampleRate() const { return 8000; }
+ virtual size_t bufferSize() const { return 320; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
+ virtual int format() const { return AudioSystem::PCM_16_BIT; }
+ virtual status_t setGain(float gain) { return NO_ERROR; }
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t standby() { return NO_ERROR; }
+ virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
+ virtual String8 getParameters(const String8& keys);
+ virtual unsigned int getInputFramesLost() const { return 0; }
+};
+
+class AudioHardwareStub : public AudioHardwareBase
+{
+public:
+ AudioHardwareStub();
+ virtual ~AudioHardwareStub();
+ virtual status_t initCheck();
+ virtual status_t setVoiceVolume(float volume);
+ virtual status_t setMasterVolume(float volume);
+
+ // mic mute
+ virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; }
+ virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; }
+
+ // create I/O streams
+ virtual AudioStreamOut* openOutputStream(
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
+ status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
+ virtual AudioStreamIn* openInputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+
+protected:
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ bool mMicMute;
+private:
+ status_t dumpInternals(int fd, const Vector<String16>& args);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_AUDIO_HARDWARE_STUB_H
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
new file mode 100644
index 0000000..19a442a
--- /dev/null
+++ b/services/audioflinger/AudioMixer.cpp
@@ -0,0 +1,915 @@
+/* //device/include/server/AudioFlinger/AudioMixer.cpp
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioMixer"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include "AudioMixer.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+static inline int16_t clamp16(int32_t sample)
+{
+ if ((sample>>15) ^ (sample>>31))
+ sample = 0x7FFF ^ (sample>>31);
+ return sample;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
+ : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate)
+{
+ mState.enabledTracks= 0;
+ mState.needsChanged = 0;
+ mState.frameCount = frameCount;
+ mState.outputTemp = 0;
+ mState.resampleTemp = 0;
+ mState.hook = process__nop;
+ track_t* t = mState.tracks;
+ for (int i=0 ; i<32 ; i++) {
+ t->needs = 0;
+ t->volume[0] = UNITY_GAIN;
+ t->volume[1] = UNITY_GAIN;
+ t->volumeInc[0] = 0;
+ t->volumeInc[1] = 0;
+ t->channelCount = 2;
+ t->enabled = 0;
+ t->format = 16;
+ t->buffer.raw = 0;
+ t->bufferProvider = 0;
+ t->hook = 0;
+ t->resampler = 0;
+ t->sampleRate = mSampleRate;
+ t->in = 0;
+ t++;
+ }
+}
+
+ AudioMixer::~AudioMixer()
+ {
+ track_t* t = mState.tracks;
+ for (int i=0 ; i<32 ; i++) {
+ delete t->resampler;
+ t++;
+ }
+ delete [] mState.outputTemp;
+ delete [] mState.resampleTemp;
+ }
+
+ int AudioMixer::getTrackName()
+ {
+ uint32_t names = mTrackNames;
+ uint32_t mask = 1;
+ int n = 0;
+ while (names & mask) {
+ mask <<= 1;
+ n++;
+ }
+ if (mask) {
+ LOGV("add track (%d)", n);
+ mTrackNames |= mask;
+ return TRACK0 + n;
+ }
+ return -1;
+ }
+
+ void AudioMixer::invalidateState(uint32_t mask)
+ {
+ if (mask) {
+ mState.needsChanged |= mask;
+ mState.hook = process__validate;
+ }
+ }
+
+ void AudioMixer::deleteTrackName(int name)
+ {
+ name -= TRACK0;
+ if (uint32_t(name) < MAX_NUM_TRACKS) {
+ LOGV("deleteTrackName(%d)", name);
+ track_t& track(mState.tracks[ name ]);
+ if (track.enabled != 0) {
+ track.enabled = 0;
+ invalidateState(1<<name);
+ }
+ if (track.resampler) {
+ // delete the resampler
+ delete track.resampler;
+ track.resampler = 0;
+ track.sampleRate = mSampleRate;
+ invalidateState(1<<name);
+ }
+ track.volumeInc[0] = 0;
+ track.volumeInc[1] = 0;
+ mTrackNames &= ~(1<<name);
+ }
+ }
+
+status_t AudioMixer::enable(int name)
+{
+ switch (name) {
+ case MIXING: {
+ if (mState.tracks[ mActiveTrack ].enabled != 1) {
+ mState.tracks[ mActiveTrack ].enabled = 1;
+ LOGV("enable(%d)", mActiveTrack);
+ invalidateState(1<<mActiveTrack);
+ }
+ } break;
+ default:
+ return NAME_NOT_FOUND;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioMixer::disable(int name)
+{
+ switch (name) {
+ case MIXING: {
+ if (mState.tracks[ mActiveTrack ].enabled != 0) {
+ mState.tracks[ mActiveTrack ].enabled = 0;
+ LOGV("disable(%d)", mActiveTrack);
+ invalidateState(1<<mActiveTrack);
+ }
+ } break;
+ default:
+ return NAME_NOT_FOUND;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioMixer::setActiveTrack(int track)
+{
+ if (uint32_t(track-TRACK0) >= MAX_NUM_TRACKS) {
+ return BAD_VALUE;
+ }
+ mActiveTrack = track - TRACK0;
+ return NO_ERROR;
+}
+
+status_t AudioMixer::setParameter(int target, int name, int value)
+{
+ switch (target) {
+ case TRACK:
+ if (name == CHANNEL_COUNT) {
+ if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) {
+ if (mState.tracks[ mActiveTrack ].channelCount != value) {
+ mState.tracks[ mActiveTrack ].channelCount = value;
+ LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value);
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+ }
+ break;
+ case RESAMPLE:
+ if (name == SAMPLE_RATE) {
+ if (value > 0) {
+ track_t& track = mState.tracks[ mActiveTrack ];
+ if (track.setResampler(uint32_t(value), mSampleRate)) {
+ LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
+ uint32_t(value));
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+ }
+ break;
+ case RAMP_VOLUME:
+ case VOLUME:
+ if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
+ track_t& track = mState.tracks[ mActiveTrack ];
+ if (track.volume[name-VOLUME0] != value) {
+ track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
+ track.volume[name-VOLUME0] = value;
+ if (target == VOLUME) {
+ track.prevVolume[name-VOLUME0] = value << 16;
+ track.volumeInc[name-VOLUME0] = 0;
+ } else {
+ int32_t d = (value<<16) - track.prevVolume[name-VOLUME0];
+ int32_t volInc = d / int32_t(mState.frameCount);
+ track.volumeInc[name-VOLUME0] = volInc;
+ if (volInc == 0) {
+ track.prevVolume[name-VOLUME0] = value << 16;
+ }
+ }
+ invalidateState(1<<mActiveTrack);
+ }
+ return NO_ERROR;
+ }
+ break;
+ }
+ return BAD_VALUE;
+}
+
+bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
+{
+ if (value!=devSampleRate || resampler) {
+ if (sampleRate != value) {
+ sampleRate = value;
+ if (resampler == 0) {
+ resampler = AudioResampler::create(
+ format, channelCount, devSampleRate);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AudioMixer::track_t::doesResample() const
+{
+ return resampler != 0;
+}
+
+inline
+void AudioMixer::track_t::adjustVolumeRamp()
+{
+ for (int i=0 ; i<2 ; i++) {
+ if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
+ ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) {
+ volumeInc[i] = 0;
+ prevVolume[i] = volume[i]<<16;
+ }
+ }
+}
+
+
+status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer)
+{
+ mState.tracks[ mActiveTrack ].bufferProvider = buffer;
+ return NO_ERROR;
+}
+
+
+
+void AudioMixer::process(void* output)
+{
+ mState.hook(&mState, output);
+}
+
+
+void AudioMixer::process__validate(state_t* state, void* output)
+{
+ LOGW_IF(!state->needsChanged,
+ "in process__validate() but nothing's invalid");
+
+ uint32_t changed = state->needsChanged;
+ state->needsChanged = 0; // clear the validation flag
+
+ // recompute which tracks are enabled / disabled
+ uint32_t enabled = 0;
+ uint32_t disabled = 0;
+ while (changed) {
+ const int i = 31 - __builtin_clz(changed);
+ const uint32_t mask = 1<<i;
+ changed &= ~mask;
+ track_t& t = state->tracks[i];
+ (t.enabled ? enabled : disabled) |= mask;
+ }
+ state->enabledTracks &= ~disabled;
+ state->enabledTracks |= enabled;
+
+ // compute everything we need...
+ int countActiveTracks = 0;
+ int all16BitsStereoNoResample = 1;
+ int resampling = 0;
+ int volumeRamp = 0;
+ uint32_t en = state->enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+
+ countActiveTracks++;
+ track_t& t = state->tracks[i];
+ uint32_t n = 0;
+ n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
+ n |= NEEDS_FORMAT_16;
+ n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
+
+ if (t.volumeInc[0]|t.volumeInc[1]) {
+ volumeRamp = 1;
+ } else if (!t.doesResample() && t.volumeRL == 0) {
+ n |= NEEDS_MUTE_ENABLED;
+ }
+ t.needs = n;
+
+ if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
+ t.hook = track__nop;
+ } else {
+ if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ all16BitsStereoNoResample = 0;
+ resampling = 1;
+ t.hook = track__genericResample;
+ } else {
+ if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
+ t.hook = track__16BitsMono;
+ all16BitsStereoNoResample = 0;
+ }
+ if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){
+ t.hook = track__16BitsStereo;
+ }
+ }
+ }
+ }
+
+ // select the processing hooks
+ state->hook = process__nop;
+ if (countActiveTracks) {
+ if (resampling) {
+ if (!state->outputTemp) {
+ state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount];
+ }
+ if (!state->resampleTemp) {
+ state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount];
+ }
+ state->hook = process__genericResampling;
+ } else {
+ if (state->outputTemp) {
+ delete [] state->outputTemp;
+ state->outputTemp = 0;
+ }
+ if (state->resampleTemp) {
+ delete [] state->resampleTemp;
+ state->resampleTemp = 0;
+ }
+ state->hook = process__genericNoResampling;
+ if (all16BitsStereoNoResample && !volumeRamp) {
+ if (countActiveTracks == 1) {
+ state->hook = process__OneTrack16BitsStereoNoResampling;
+ }
+ }
+ }
+ }
+
+ LOGV("mixer configuration change: %d activeTracks (%08x) "
+ "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d",
+ countActiveTracks, state->enabledTracks,
+ all16BitsStereoNoResample, resampling, volumeRamp);
+
+ state->hook(state, output);
+
+ // Now that the volume ramp has been done, set optimal state and
+ // track hooks for subsequent mixer process
+ if (countActiveTracks) {
+ int allMuted = 1;
+ uint32_t en = state->enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ if (!t.doesResample() && t.volumeRL == 0)
+ {
+ t.needs |= NEEDS_MUTE_ENABLED;
+ t.hook = track__nop;
+ } else {
+ allMuted = 0;
+ }
+ }
+ if (allMuted) {
+ state->hook = process__nop;
+ } else if (!resampling && all16BitsStereoNoResample) {
+ if (countActiveTracks == 1) {
+ state->hook = process__OneTrack16BitsStereoNoResampling;
+ }
+ }
+ }
+}
+
+static inline
+int32_t mulAdd(int16_t in, int16_t v, int32_t a)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ asm( "smlabb %[out], %[in], %[v], %[a] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
+ : );
+ return out;
+#else
+ return a + in * int32_t(v);
+#endif
+}
+
+static inline
+int32_t mul(int16_t in, int16_t v)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ asm( "smulbb %[out], %[in], %[v] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v)
+ : );
+ return out;
+#else
+ return in * int32_t(v);
+#endif
+}
+
+static inline
+int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ if (left) {
+ asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a)
+ : );
+ } else {
+ asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a)
+ : );
+ }
+ return out;
+#else
+ if (left) {
+ return a + int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF);
+ } else {
+ return a + int16_t(inRL>>16) * int16_t(vRL>>16);
+ }
+#endif
+}
+
+static inline
+int32_t mulRL(int left, uint32_t inRL, uint32_t vRL)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ if (left) {
+ asm( "smulbb %[out], %[inRL], %[vRL] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [vRL]"r"(vRL)
+ : );
+ } else {
+ asm( "smultt %[out], %[inRL], %[vRL] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [vRL]"r"(vRL)
+ : );
+ }
+ return out;
+#else
+ if (left) {
+ return int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF);
+ } else {
+ return int16_t(inRL>>16) * int16_t(vRL>>16);
+ }
+#endif
+}
+
+
+void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+{
+ t->resampler->setSampleRate(t->sampleRate);
+
+ // ramp gain - resample to temp buffer and scale/mix in 2nd step
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
+ memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+ t->resampler->resample(temp, outFrameCount, t->bufferProvider);
+ volumeRampStereo(t, out, outFrameCount, temp);
+ }
+
+ // constant gain
+ else {
+ t->resampler->setVolume(t->volume[0], t->volume[1]);
+ t->resampler->resample(out, outFrameCount, t->bufferProvider);
+ }
+}
+
+void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
+{
+}
+
+void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+{
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+
+ //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ // ramp volume
+ do {
+ *out++ += (vl >> 16) * (*temp++ >> 12);
+ *out++ += (vr >> 16) * (*temp++ >> 12);
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp();
+}
+
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+{
+ int16_t const *in = static_cast<int16_t const *>(t->in);
+
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ *out++ += (vl >> 16) * (int32_t) *in++;
+ *out++ += (vr >> 16) * (int32_t) *in++;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp();
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = t->volumeRL;
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ t->in = in;
+}
+
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
+{
+ int16_t const *in = static_cast<int16_t const *>(t->in);
+
+ // ramp gain
+ if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
+ int32_t vl = t->prevVolume[0];
+ int32_t vr = t->prevVolume[1];
+ const int32_t vlInc = t->volumeInc[0];
+ const int32_t vrInc = t->volumeInc[1];
+
+ // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ t->prevVolume[0] = vl;
+ t->prevVolume[1] = vr;
+ t->adjustVolumeRamp();
+ }
+ // constant gain
+ else {
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ t->in = in;
+}
+
+void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
+{
+ for (size_t i=0 ; i<c ; i++) {
+ int32_t l = *sums++;
+ int32_t r = *sums++;
+ int32_t nl = l >> 12;
+ int32_t nr = r >> 12;
+ l = clamp16(nl);
+ r = clamp16(nr);
+ *out++ = (r<<16) | (l & 0xFFFF);
+ }
+}
+
+// no-op case
+void AudioMixer::process__nop(state_t* state, void* output)
+{
+ // this assumes output 16 bits stereo, no resampling
+ memset(output, 0, state->frameCount*4);
+ uint32_t en = state->enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ size_t outFrames = state->frameCount;
+ while (outFrames) {
+ t.buffer.frameCount = outFrames;
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ if (!t.buffer.raw) break;
+ outFrames -= t.buffer.frameCount;
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ }
+ }
+}
+
+// generic code without resampling
+void AudioMixer::process__genericNoResampling(state_t* state, void* output)
+{
+ int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
+
+ // acquire each track's buffer
+ uint32_t enabledTracks = state->enabledTracks;
+ uint32_t en = enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ t.buffer.frameCount = state->frameCount;
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.frameCount = t.buffer.frameCount;
+ t.in = t.buffer.raw;
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL)
+ enabledTracks &= ~(1<<i);
+ }
+
+ // this assumes output 16 bits stereo, no resampling
+ int32_t* out = static_cast<int32_t*>(output);
+ size_t numFrames = state->frameCount;
+ do {
+ memset(outTemp, 0, sizeof(outTemp));
+
+ en = enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ size_t outFrames = BLOCKSIZE;
+
+ while (outFrames) {
+ size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
+ if (inFrames) {
+ (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp);
+ t.frameCount -= inFrames;
+ outFrames -= inFrames;
+ }
+ if (t.frameCount == 0 && outFrames) {
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames);
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ if (t.in == NULL) {
+ enabledTracks &= ~(1<<i);
+ break;
+ }
+ t.frameCount = t.buffer.frameCount;
+ }
+ }
+ }
+
+ ditherAndClamp(out, outTemp, BLOCKSIZE);
+ out += BLOCKSIZE;
+ numFrames -= BLOCKSIZE;
+ } while (numFrames);
+
+
+ // release each track's buffer
+ en = enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ }
+}
+
+// generic code with resampling
+void AudioMixer::process__genericResampling(state_t* state, void* output)
+{
+ int32_t* const outTemp = state->outputTemp;
+ const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
+ memset(outTemp, 0, size);
+
+ int32_t* out = static_cast<int32_t*>(output);
+ size_t numFrames = state->frameCount;
+
+ uint32_t en = state->enabledTracks;
+ while (en) {
+ const int i = 31 - __builtin_clz(en);
+ en &= ~(1<<i);
+ track_t& t = state->tracks[i];
+
+ // this is a little goofy, on the resampling case we don't
+ // acquire/release the buffers because it's done by
+ // the resampler.
+ if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
+ (t.hook)(&t, outTemp, numFrames, state->resampleTemp);
+ } else {
+
+ size_t outFrames = numFrames;
+
+ while (outFrames) {
+ t.buffer.frameCount = outFrames;
+ t.bufferProvider->getNextBuffer(&t.buffer);
+ t.in = t.buffer.raw;
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL) break;
+
+ (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp);
+ outFrames -= t.buffer.frameCount;
+ t.bufferProvider->releaseBuffer(&t.buffer);
+ }
+ }
+ }
+
+ ditherAndClamp(out, outTemp, numFrames);
+}
+
+// one track, 16 bits stereo without resampling is the most common case
+void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
+{
+ const int i = 31 - __builtin_clz(state->enabledTracks);
+ const track_t& t = state->tracks[i];
+
+ AudioBufferProvider::Buffer& b(t.buffer);
+
+ int32_t* out = static_cast<int32_t*>(output);
+ size_t numFrames = state->frameCount;
+
+ const int16_t vl = t.volume[0];
+ const int16_t vr = t.volume[1];
+ const uint32_t vrl = t.volumeRL;
+ while (numFrames) {
+ b.frameCount = numFrames;
+ t.bufferProvider->getNextBuffer(&b);
+ int16_t const *in = b.i16;
+
+ // in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (in == NULL || ((unsigned long)in & 3)) {
+ memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t));
+ LOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: buffer %p track %d, channels %d, needs %08x",
+ in, i, t.channelCount, t.needs);
+ return;
+ }
+ size_t outFrames = b.frameCount;
+
+ if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
+ // volume is boosted, so we might need to clamp even though
+ // we process only one track.
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ // clamping...
+ l = clamp16(l);
+ r = clamp16(r);
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ } else {
+ do {
+ uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl) >> 12;
+ int32_t r = mulRL(0, rl, vrl) >> 12;
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+ }
+ numFrames -= b.frameCount;
+ t.bufferProvider->releaseBuffer(&b);
+ }
+}
+
+// 2 tracks is also a common case
+void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output)
+{
+ int i;
+ uint32_t en = state->enabledTracks;
+
+ i = 31 - __builtin_clz(en);
+ const track_t& t0 = state->tracks[i];
+ AudioBufferProvider::Buffer& b0(t0.buffer);
+
+ en &= ~(1<<i);
+ i = 31 - __builtin_clz(en);
+ const track_t& t1 = state->tracks[i];
+ AudioBufferProvider::Buffer& b1(t1.buffer);
+
+ int16_t const *in0;
+ const int16_t vl0 = t0.volume[0];
+ const int16_t vr0 = t0.volume[1];
+ size_t frameCount0 = 0;
+
+ int16_t const *in1;
+ const int16_t vl1 = t1.volume[0];
+ const int16_t vr1 = t1.volume[1];
+ size_t frameCount1 = 0;
+
+ int32_t* out = static_cast<int32_t*>(output);
+ size_t numFrames = state->frameCount;
+ int16_t const *buff = NULL;
+
+
+ while (numFrames) {
+
+ if (frameCount0 == 0) {
+ b0.frameCount = numFrames;
+ t0.bufferProvider->getNextBuffer(&b0);
+ if (b0.i16 == NULL) {
+ if (buff == NULL) {
+ buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
+ }
+ in0 = buff;
+ b0.frameCount = numFrames;
+ } else {
+ in0 = b0.i16;
+ }
+ frameCount0 = b0.frameCount;
+ }
+ if (frameCount1 == 0) {
+ b1.frameCount = numFrames;
+ t1.bufferProvider->getNextBuffer(&b1);
+ if (b1.i16 == NULL) {
+ if (buff == NULL) {
+ buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
+ }
+ in1 = buff;
+ b1.frameCount = numFrames;
+ } else {
+ in1 = b1.i16;
+ }
+ frameCount1 = b1.frameCount;
+ }
+
+ size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
+
+ numFrames -= outFrames;
+ frameCount0 -= outFrames;
+ frameCount1 -= outFrames;
+
+ do {
+ int32_t l0 = *in0++;
+ int32_t r0 = *in0++;
+ l0 = mul(l0, vl0);
+ r0 = mul(r0, vr0);
+ int32_t l = *in1++;
+ int32_t r = *in1++;
+ l = mulAdd(l, vl1, l0) >> 12;
+ r = mulAdd(r, vr1, r0) >> 12;
+ // clamping...
+ l = clamp16(l);
+ r = clamp16(r);
+ *out++ = (r<<16) | (l & 0xFFFF);
+ } while (--outFrames);
+
+ if (frameCount0 == 0) {
+ t0.bufferProvider->releaseBuffer(&b0);
+ }
+ if (frameCount1 == 0) {
+ t1.bufferProvider->releaseBuffer(&b1);
+ }
+ }
+
+ if (buff != NULL) {
+ delete [] buff;
+ }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
new file mode 100644
index 0000000..15766cd
--- /dev/null
+++ b/services/audioflinger/AudioMixer.h
@@ -0,0 +1,193 @@
+/* //device/include/server/AudioFlinger/AudioMixer.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_MIXER_H
+#define ANDROID_AUDIO_MIXER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "AudioBufferProvider.h"
+#include "AudioResampler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+// ----------------------------------------------------------------------------
+
+class AudioMixer
+{
+public:
+ AudioMixer(size_t frameCount, uint32_t sampleRate);
+
+ ~AudioMixer();
+
+ static const uint32_t MAX_NUM_TRACKS = 32;
+ static const uint32_t MAX_NUM_CHANNELS = 2;
+
+ static const uint16_t UNITY_GAIN = 0x1000;
+
+ enum { // names
+
+ // track units (32 units)
+ TRACK0 = 0x1000,
+
+ // enable/disable
+ MIXING = 0x2000,
+
+ // setParameter targets
+ TRACK = 0x3000,
+ RESAMPLE = 0x3001,
+ RAMP_VOLUME = 0x3002, // ramp to new volume
+ VOLUME = 0x3003, // don't ramp
+
+ // set Parameter names
+ // for target TRACK
+ CHANNEL_COUNT = 0x4000,
+ FORMAT = 0x4001,
+ // for TARGET RESAMPLE
+ SAMPLE_RATE = 0x4100,
+ // for TARGET VOLUME (8 channels max)
+ VOLUME0 = 0x4200,
+ VOLUME1 = 0x4201,
+ };
+
+
+ int getTrackName();
+ void deleteTrackName(int name);
+
+ status_t enable(int name);
+ status_t disable(int name);
+
+ status_t setActiveTrack(int track);
+ status_t setParameter(int target, int name, int value);
+
+ status_t setBufferProvider(AudioBufferProvider* bufferProvider);
+ void process(void* output);
+
+ uint32_t trackNames() const { return mTrackNames; }
+
+ static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c);
+
+private:
+
+ enum {
+ NEEDS_CHANNEL_COUNT__MASK = 0x00000003,
+ NEEDS_FORMAT__MASK = 0x000000F0,
+ NEEDS_MUTE__MASK = 0x00000100,
+ NEEDS_RESAMPLE__MASK = 0x00001000,
+ };
+
+ enum {
+ NEEDS_CHANNEL_1 = 0x00000000,
+ NEEDS_CHANNEL_2 = 0x00000001,
+
+ NEEDS_FORMAT_16 = 0x00000010,
+
+ NEEDS_MUTE_DISABLED = 0x00000000,
+ NEEDS_MUTE_ENABLED = 0x00000100,
+
+ NEEDS_RESAMPLE_DISABLED = 0x00000000,
+ NEEDS_RESAMPLE_ENABLED = 0x00001000,
+ };
+
+ static inline int32_t applyVolume(int32_t in, int32_t v) {
+ return in * v;
+ }
+
+
+ struct state_t;
+
+ typedef void (*mix_t)(state_t* state, void* output);
+
+ static const int BLOCKSIZE = 16; // 4 cache lines
+
+ struct track_t {
+ uint32_t needs;
+
+ union {
+ int16_t volume[2]; // [0]3.12 fixed point
+ int32_t volumeRL;
+ };
+
+ int32_t prevVolume[2];
+
+ int32_t volumeInc[2];
+
+ uint16_t frameCount;
+
+ uint8_t channelCount : 4;
+ uint8_t enabled : 1;
+ uint8_t reserved0 : 3;
+ uint8_t format;
+
+ AudioBufferProvider* bufferProvider;
+ mutable AudioBufferProvider::Buffer buffer;
+
+ void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp);
+ void const* in; // current location in buffer
+
+ AudioResampler* resampler;
+ uint32_t sampleRate;
+
+ bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
+ bool doesResample() const;
+ void adjustVolumeRamp();
+ };
+
+ // pad to 32-bytes to fill cache line
+ struct state_t {
+ uint32_t enabledTracks;
+ uint32_t needsChanged;
+ size_t frameCount;
+ mix_t hook;
+ int32_t *outputTemp;
+ int32_t *resampleTemp;
+ int32_t reserved[2];
+ track_t tracks[32]; __attribute__((aligned(32)));
+ };
+
+ int mActiveTrack;
+ uint32_t mTrackNames;
+ const uint32_t mSampleRate;
+
+ state_t mState __attribute__((aligned(32)));
+
+ void invalidateState(uint32_t mask);
+
+ static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+ static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+ static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
+ static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+ static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
+
+ static void process__validate(state_t* state, void* output);
+ static void process__nop(state_t* state, void* output);
+ static void process__genericNoResampling(state_t* state, void* output);
+ static void process__genericResampling(state_t* state, void* output);
+ static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output);
+ static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_AUDIO_MIXER_H
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
new file mode 100644
index 0000000..c8b3f48
--- /dev/null
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -0,0 +1,1972 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioPolicyManagerBase"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+{
+
+ LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
+
+ // connect/disconnect only 1 device at a time
+ if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
+
+ if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
+ LOGE("setDeviceConnectionState() invalid address: %s", device_address);
+ return BAD_VALUE;
+ }
+
+ // handle output devices
+ if (AudioSystem::isOutputDevice(device)) {
+
+#ifndef WITH_A2DP
+ if (AudioSystem::isA2dpDevice(device)) {
+ LOGE("setDeviceConnectionState() invalid device: %x", device);
+ return BAD_VALUE;
+ }
+#endif
+
+ switch (state)
+ {
+ // handle output device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE:
+ if (mAvailableOutputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %x", device);
+ return INVALID_OPERATION;
+ }
+ LOGV("setDeviceConnectionState() connecting device %x", device);
+
+ // register new device as available
+ mAvailableOutputDevices |= device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device connection
+ if (AudioSystem::isA2dpDevice(device)) {
+ status_t status = handleA2dpConnection(device, device_address);
+ if (status != NO_ERROR) {
+ mAvailableOutputDevices &= ~device;
+ return status;
+ }
+ } else
+#endif
+ {
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
+ // keep track of SCO device address
+ mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 &&
+ mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+#endif
+ }
+ }
+ break;
+ // handle output device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+ if (!(mAvailableOutputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %x", device);
+ return INVALID_OPERATION;
+ }
+
+
+ LOGV("setDeviceConnectionState() disconnecting device %x", device);
+ // remove device from available output devices
+ mAvailableOutputDevices &= ~device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device disconnection
+ if (AudioSystem::isA2dpDevice(device)) {
+ status_t status = handleA2dpDisconnection(device, device_address);
+ if (status != NO_ERROR) {
+ mAvailableOutputDevices |= device;
+ return status;
+ }
+ } else
+#endif
+ {
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ mScoDeviceAddress = "";
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 &&
+ mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+#endif
+ }
+ }
+ } break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+
+ // request routing change if necessary
+ uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+ // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
+ if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
+ closeA2dpOutputs();
+ }
+#endif
+ updateDeviceForStrategy();
+ setOutputDevice(mHardwareOutput, newDevice);
+
+ if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
+ device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+ } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
+ device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
+ device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
+ device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ } else {
+ return NO_ERROR;
+ }
+ }
+ // handle input devices
+ if (AudioSystem::isInputDevice(device)) {
+
+ switch (state)
+ {
+ // handle input device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE: {
+ if (mAvailableInputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices |= device;
+ }
+ break;
+
+ // handle input device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+ if (!(mAvailableInputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices &= ~device;
+ } break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+
+ audio_io_handle_t activeInput = getActiveInput();
+ if (activeInput != 0) {
+ AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
+ uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
+ if (newDevice != inputDesc->mDevice) {
+ LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
+ inputDesc->mDevice, newDevice, activeInput);
+ inputDesc->mDevice = newDevice;
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
+ mpClientInterface->setParameters(activeInput, param.toString());
+ }
+ }
+
+ return NO_ERROR;
+ }
+
+ LOGW("setDeviceConnectionState() invalid device: %x", device);
+ return BAD_VALUE;
+}
+
+AudioSystem::device_connection_state AudioPolicyManagerBase::getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ String8 address = String8(device_address);
+ if (AudioSystem::isOutputDevice(device)) {
+ if (device & mAvailableOutputDevices) {
+#ifdef WITH_A2DP
+ if (AudioSystem::isA2dpDevice(device) &&
+ address != "" && mA2dpDeviceAddress != address) {
+ return state;
+ }
+#endif
+ if (AudioSystem::isBluetoothScoDevice(device) &&
+ address != "" && mScoDeviceAddress != address) {
+ return state;
+ }
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ } else if (AudioSystem::isInputDevice(device)) {
+ if (device & mAvailableInputDevices) {
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ }
+
+ return state;
+}
+
+void AudioPolicyManagerBase::setPhoneState(int state)
+{
+ LOGV("setPhoneState() state %d", state);
+ uint32_t newDevice = 0;
+ if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ LOGW("setPhoneState() invalid state %d", state);
+ return;
+ }
+
+ if (state == mPhoneState ) {
+ LOGW("setPhoneState() setting same state %d", state);
+ return;
+ }
+
+ // if leaving call state, handle special case of active streams
+ // pertaining to sonification strategy see handleIncallSonification()
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ LOGV("setPhoneState() in call state management: new state is %d", state);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, false, true);
+ }
+ }
+
+ // store previous phone state for management of sonification strategy below
+ int oldState = mPhoneState;
+ mPhoneState = state;
+ bool force = false;
+
+ // are we entering or starting a call
+ if ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) {
+ LOGV(" Entering call in setPhoneState()");
+ // force routing command to audio hardware when starting a call
+ // even if no device change is needed
+ force = true;
+ } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) {
+ LOGV(" Exiting call in setPhoneState()");
+ // force routing command to audio hardware when exiting a call
+ // even if no device change is needed
+ force = true;
+ }
+
+ // check for device and output changes triggered by new phone state
+ newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+ // suspend A2DP output if a SCO device is present.
+ if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
+ if (oldState == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ } else if (state == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+#endif
+ updateDeviceForStrategy();
+
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+ // force routing command to audio hardware when ending call
+ // even if no device change is needed
+ if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) {
+ newDevice = hwOutputDesc->device();
+ }
+
+ // when changing from ring tone to in call mode, mute the ringing tone
+ // immediately and delay the route change to avoid sending the ring tone
+ // tail into the earpiece or headset.
+ int delayMs = 0;
+ if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) {
+ // delay the device change command by twice the output latency to have some margin
+ // and be sure that audio buffers not yet affected by the mute are out when
+ // we actually apply the route change
+ delayMs = hwOutputDesc->mLatency*2;
+ setStreamMute(AudioSystem::RING, true, mHardwareOutput);
+ }
+
+ // change routing is necessary
+ setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
+
+ // if entering in call state, handle special case of active streams
+ // pertaining to sonification strategy see handleIncallSonification()
+ if (state == AudioSystem::MODE_IN_CALL) {
+ LOGV("setPhoneState() in call state management: new state is %d", state);
+ // unmute the ringing tone after a sufficient delay if it was muted before
+ // setting output device above
+ if (oldState == AudioSystem::MODE_RINGTONE) {
+ setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
+ }
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, true, true);
+ }
+ }
+
+ // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
+ if (state == AudioSystem::MODE_RINGTONE &&
+ (hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
+ (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
+ mLimitRingtoneVolume = true;
+ } else {
+ mLimitRingtoneVolume = false;
+ }
+}
+
+void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+ mRingerMode = mode;
+}
+
+void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
+
+ bool forceVolumeReeval = false;
+ switch(usage) {
+ case AudioSystem::FOR_COMMUNICATION:
+ if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
+ config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ break;
+ case AudioSystem::FOR_MEDIA:
+ if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
+ config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ break;
+ case AudioSystem::FOR_RECORD:
+ if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+ config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ break;
+ case AudioSystem::FOR_DOCK:
+ if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
+ config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+ LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+ }
+ forceVolumeReeval = true;
+ mForceUse[usage] = config;
+ break;
+ default:
+ LOGW("setForceUse() invalid usage %d", usage);
+ break;
+ }
+
+ // check for device and output changes triggered by new phone state
+ uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+#endif
+ updateDeviceForStrategy();
+ setOutputDevice(mHardwareOutput, newDevice);
+ if (forceVolumeReeval) {
+ applyStreamVolumes(mHardwareOutput, newDevice);
+ }
+}
+
+AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
+{
+ return mForceUse[usage];
+}
+
+void AudioPolicyManagerBase::setSystemProperty(const char* property, const char* value)
+{
+ LOGV("setSystemProperty() property %s, value %s", property, value);
+ if (strcmp(property, "ro.camera.sound.forced") == 0) {
+ if (atoi(value)) {
+ LOGV("ENFORCED_AUDIBLE cannot be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
+ } else {
+ LOGV("ENFORCED_AUDIBLE can be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
+ }
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+{
+ audio_io_handle_t output = 0;
+ uint32_t latency = 0;
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+ uint32_t device = getDeviceForStrategy(strategy);
+ LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mCurOutput != 0) {
+ LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
+ mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
+
+ if (mTestOutputs[mCurOutput] == 0) {
+ LOGV("getOutput() opening test output");
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = mTestDevice;
+ outputDesc->mSamplingRate = mTestSamplingRate;
+ outputDesc->mFormat = mTestFormat;
+ outputDesc->mChannels = mTestChannels;
+ outputDesc->mLatency = mTestLatencyMs;
+ outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
+ outputDesc->mRefCount[stream] = 0;
+ mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mTestOutputs[mCurOutput]) {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"),mCurOutput);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
+ addOutput(mTestOutputs[mCurOutput], outputDesc);
+ }
+ }
+ return mTestOutputs[mCurOutput];
+ }
+#endif //AUDIO_POLICY_TEST
+
+ // open a direct output if required by specified parameters
+ if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
+
+ LOGV("getOutput() opening direct output device %x", device);
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = device;
+ outputDesc->mSamplingRate = samplingRate;
+ outputDesc->mFormat = format;
+ outputDesc->mChannels = channels;
+ outputDesc->mLatency = 0;
+ outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
+ outputDesc->mRefCount[stream] = 0;
+ output = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ // only accept an output with the requeted parameters
+ if (output == 0 ||
+ (samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
+ (format != 0 && format != outputDesc->mFormat) ||
+ (channels != 0 && channels != outputDesc->mChannels)) {
+ LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ if (output != 0) {
+ mpClientInterface->closeOutput(output);
+ }
+ delete outputDesc;
+ return 0;
+ }
+ addOutput(output, outputDesc);
+ return output;
+ }
+
+ if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO &&
+ channels != AudioSystem::CHANNEL_OUT_STEREO) {
+ return 0;
+ }
+ // open a non direct output
+
+ // get which output is suitable for the specified stream. The actual routing change will happen
+ // when startOutput() will be called
+ uint32_t a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP;
+ if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
+#ifdef WITH_A2DP
+ if (a2dpUsedForSonification() && a2dpDevice != 0) {
+ // if playing on 2 devices among which one is A2DP, use duplicated output
+ LOGV("getOutput() using duplicated output");
+ LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device);
+ output = mDuplicatedOutput;
+ } else
+#endif
+ {
+ // if playing on 2 devices among which none is A2DP, use hardware output
+ output = mHardwareOutput;
+ }
+ LOGV("getOutput() using output %d for 2 devices %x", output, device);
+ } else {
+#ifdef WITH_A2DP
+ if (a2dpDevice != 0) {
+ // if playing on A2DP device, use a2dp output
+ LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device);
+ output = mA2dpOutput;
+ } else
+#endif
+ {
+ // if playing on not A2DP device, use hardware output
+ output = mHardwareOutput;
+ }
+ }
+
+
+ LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
+ stream, samplingRate, format, channels, flags);
+
+ return output;
+}
+
+status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("startOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("startOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
+ setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+ }
+#endif
+
+ // incremenent usage count for this stream on the requested output:
+ // NOTE that the usage count is the same for duplicated output and hardware output which is
+ // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
+ outputDesc->changeRefCount(stream, 1);
+
+ setOutputDevice(output, getNewDevice(output));
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, true, false);
+ }
+
+ // apply volume rules for current stream and device if necessary
+ checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("stopOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("stopOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, false, false);
+ }
+
+ if (outputDesc->mRefCount[stream] > 0) {
+ // decrement usage count of this stream on the output
+ outputDesc->changeRefCount(stream, -1);
+ // store time at which the last music track was stopped - see computeVolume()
+ if (stream == AudioSystem::MUSIC) {
+ mMusicStopTime = systemTime();
+ }
+
+ setOutputDevice(output, getNewDevice(output));
+
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
+ setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput, mOutputs.valueFor(mHardwareOutput)->mLatency*2);
+ }
+#endif
+ if (output != mHardwareOutput) {
+ setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
+ }
+ return NO_ERROR;
+ } else {
+ LOGW("stopOutput() refcount is already 0 for output %d", output);
+ return INVALID_OPERATION;
+ }
+}
+
+void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
+{
+ LOGV("releaseOutput() %d", output);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("releaseOutput() releasing unknown output %d", output);
+ return;
+ }
+
+#ifdef AUDIO_POLICY_TEST
+ int testIndex = testOutputIndex(output);
+ if (testIndex != 0) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ if (outputDesc->refCount() == 0) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ mTestOutputs[testIndex] = 0;
+ }
+ return;
+ }
+#endif //AUDIO_POLICY_TEST
+
+ if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ audio_io_handle_t input = 0;
+ uint32_t device = getDeviceForInputSource(inputSource);
+
+ LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
+
+ if (device == 0) {
+ return 0;
+ }
+
+ // adapt channel selection to input source
+ switch(inputSource) {
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;
+ break;
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;
+ break;
+ case AUDIO_SOURCE_VOICE_CALL:
+ channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);
+ break;
+ default:
+ break;
+ }
+
+ AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
+
+ inputDesc->mInputSource = inputSource;
+ inputDesc->mDevice = device;
+ inputDesc->mSamplingRate = samplingRate;
+ inputDesc->mFormat = format;
+ inputDesc->mChannels = channels;
+ inputDesc->mAcoustics = acoustics;
+ inputDesc->mRefCount = 0;
+ input = mpClientInterface->openInput(&inputDesc->mDevice,
+ &inputDesc->mSamplingRate,
+ &inputDesc->mFormat,
+ &inputDesc->mChannels,
+ inputDesc->mAcoustics);
+
+ // only accept input with the exact requested set of parameters
+ if (input == 0 ||
+ (samplingRate != inputDesc->mSamplingRate) ||
+ (format != inputDesc->mFormat) ||
+ (channels != inputDesc->mChannels)) {
+ LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ if (input != 0) {
+ mpClientInterface->closeInput(input);
+ }
+ delete inputDesc;
+ return 0;
+ }
+ mInputs.add(input, inputDesc);
+ return input;
+}
+
+status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input)
+{
+ LOGV("startInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("startInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mTestInput == 0)
+#endif //AUDIO_POLICY_TEST
+ {
+ // refuse 2 active AudioRecord clients at the same time
+ if (getActiveInput() != 0) {
+ LOGW("startInput() input %d failed: other input already started", input);
+ return INVALID_OPERATION;
+ }
+ }
+
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
+
+ // use Voice Recognition mode or not for this input based on input source
+ int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0;
+ param.addInt(String8("vr_mode"), vr_enabled);
+ LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled);
+
+ mpClientInterface->setParameters(input, param.toString());
+
+ inputDesc->mRefCount = 1;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::stopInput(audio_io_handle_t input)
+{
+ LOGV("stopInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("stopInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+ if (inputDesc->mRefCount == 0) {
+ LOGW("stopInput() input %d already stopped", input);
+ return INVALID_OPERATION;
+ } else {
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), 0);
+ mpClientInterface->setParameters(input, param.toString());
+ inputDesc->mRefCount = 0;
+ return NO_ERROR;
+ }
+}
+
+void AudioPolicyManagerBase::releaseInput(audio_io_handle_t input)
+{
+ LOGV("releaseInput() %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("releaseInput() releasing unknown input %d", input);
+ return;
+ }
+ mpClientInterface->closeInput(input);
+ delete mInputs.valueAt(index);
+ mInputs.removeItem(input);
+ LOGV("releaseInput() exit");
+}
+
+void AudioPolicyManagerBase::initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
+ if (indexMin < 0 || indexMin >= indexMax) {
+ LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax);
+ return;
+ }
+ mStreams[stream].mIndexMin = indexMin;
+ mStreams[stream].mIndexMax = indexMax;
+}
+
+status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+ if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+ return BAD_VALUE;
+ }
+
+ // Force max volume if stream cannot be muted
+ if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
+
+ LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
+ mStreams[stream].mIndexCur = index;
+
+ // compute and apply stream volume on all outputs according to connected device
+ status_t status = NO_ERROR;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
+ if (volStatus != NO_ERROR) {
+ status = volStatus;
+ }
+ }
+ return status;
+}
+
+status_t AudioPolicyManagerBase::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+ if (index == 0) {
+ return BAD_VALUE;
+ }
+ LOGV("getStreamVolumeIndex() stream %d", stream);
+ *index = mStreams[stream].mIndexCur;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
+ result.append(buffer);
+#ifdef WITH_A2DP
+ snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
+ result.append(buffer);
+#endif
+ snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ snprintf(buffer, SIZE, "\nOutputs dump:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mOutputs.valueAt(i)->dump(fd);
+ }
+
+ snprintf(buffer, SIZE, "\nInputs dump:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mInputs.valueAt(i)->dump(fd);
+ }
+
+ snprintf(buffer, SIZE, "\nStreams dump:\n");
+ write(fd, buffer, strlen(buffer));
+ snprintf(buffer, SIZE, " Stream Index Min Index Max Index Cur Can be muted\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ snprintf(buffer, SIZE, " %02d", i);
+ mStreams[i].dump(buffer + 3, SIZE);
+ write(fd, buffer, strlen(buffer));
+ }
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManagerBase
+// ----------------------------------------------------------------------------
+
+AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
+ :
+#ifdef AUDIO_POLICY_TEST
+ Thread(false),
+#endif //AUDIO_POLICY_TEST
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false)
+{
+ mpClientInterface = clientInterface;
+
+ for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
+ mForceUse[i] = AudioSystem::FORCE_NONE;
+ }
+
+ // devices available by default are speaker, ear piece and microphone
+ mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
+ AudioSystem::DEVICE_OUT_SPEAKER;
+ mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+
+#ifdef WITH_A2DP
+ mA2dpOutput = 0;
+ mDuplicatedOutput = 0;
+ mA2dpDeviceAddress = String8("");
+#endif
+ mScoDeviceAddress = String8("");
+
+ // open hardware output
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+ mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ if (mHardwareOutput == 0) {
+ LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ addOutput(mHardwareOutput, outputDesc);
+ setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
+ }
+
+ updateDeviceForStrategy();
+#ifdef AUDIO_POLICY_TEST
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+
+ mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
+ mTestSamplingRate = 44100;
+ mTestFormat = AudioSystem::PCM_16_BIT;
+ mTestChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ mTestLatencyMs = 0;
+ mCurOutput = 0;
+ mDirectOutput = false;
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ mTestOutputs[i] = 0;
+ }
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "AudioPolicyManagerTest");
+ run(buffer, ANDROID_PRIORITY_AUDIO);
+#endif //AUDIO_POLICY_TEST
+}
+
+AudioPolicyManagerBase::~AudioPolicyManagerBase()
+{
+#ifdef AUDIO_POLICY_TEST
+ exit();
+#endif //AUDIO_POLICY_TEST
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ mpClientInterface->closeOutput(mOutputs.keyAt(i));
+ delete mOutputs.valueAt(i);
+ }
+ mOutputs.clear();
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ mpClientInterface->closeInput(mInputs.keyAt(i));
+ delete mInputs.valueAt(i);
+ }
+ mInputs.clear();
+}
+
+#ifdef AUDIO_POLICY_TEST
+bool AudioPolicyManagerBase::threadLoop()
+{
+ LOGV("entering threadLoop()");
+ while (!exitPending())
+ {
+ String8 command;
+ int valueInt;
+ String8 value;
+
+ Mutex::Autolock _l(mLock);
+ mWaitWorkCV.waitRelative(mLock, milliseconds(50));
+
+ command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
+ AudioParameter param = AudioParameter(command);
+
+ if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
+ valueInt != 0) {
+ LOGV("Test command %s received", command.string());
+ String8 target;
+ if (param.get(String8("target"), target) != NO_ERROR) {
+ target = "Manager";
+ }
+ if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_output"));
+ mCurOutput = valueInt;
+ }
+ if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_direct"));
+ if (value == "false") {
+ mDirectOutput = false;
+ } else if (value == "true") {
+ mDirectOutput = true;
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_input"));
+ mTestInput = valueInt;
+ }
+
+ if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_format"));
+ int format = AudioSystem::INVALID_FORMAT;
+ if (value == "PCM 16 bits") {
+ format = AudioSystem::PCM_16_BIT;
+ } else if (value == "PCM 8 bits") {
+ format = AudioSystem::PCM_8_BIT;
+ } else if (value == "Compressed MP3") {
+ format = AudioSystem::MP3;
+ }
+ if (format != AudioSystem::INVALID_FORMAT) {
+ if (target == "Manager") {
+ mTestFormat = format;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("format"), format);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_channels"));
+ int channels = 0;
+
+ if (value == "Channels Stereo") {
+ channels = AudioSystem::CHANNEL_OUT_STEREO;
+ } else if (value == "Channels Mono") {
+ channels = AudioSystem::CHANNEL_OUT_MONO;
+ }
+ if (channels != 0) {
+ if (target == "Manager") {
+ mTestChannels = channels;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("channels"), channels);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_sampleRate"));
+ if (valueInt >= 0 && valueInt <= 96000) {
+ int samplingRate = valueInt;
+ if (target == "Manager") {
+ mTestSamplingRate = samplingRate;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("sampling_rate"), samplingRate);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+
+ if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_reopen"));
+
+ mpClientInterface->closeOutput(mHardwareOutput);
+ delete mOutputs.valueFor(mHardwareOutput);
+ mOutputs.removeItem(mHardwareOutput);
+
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+ mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mHardwareOutput == 0) {
+ LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+ addOutput(mHardwareOutput, outputDesc);
+ }
+ }
+
+
+ mpClientInterface->setParameters(0, String8("test_cmd_policy="));
+ }
+ }
+ return false;
+}
+
+void AudioPolicyManagerBase::exit()
+{
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output)
+{
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ if (output == mTestOutputs[i]) return i;
+ }
+ return 0;
+}
+#endif //AUDIO_POLICY_TEST
+
+// ---
+
+void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
+{
+ outputDesc->mId = id;
+ mOutputs.add(id, outputDesc);
+}
+
+
+#ifdef WITH_A2DP
+status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ // when an A2DP device is connected, open an A2DP and a duplicated output
+ LOGV("opening A2DP output for device %s", device_address);
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = device;
+ mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mA2dpOutput) {
+ // add A2DP output descriptor
+ addOutput(mA2dpOutput, outputDesc);
+ // set initial stream volume for A2DP device
+ applyStreamVolumes(mA2dpOutput, device);
+ if (a2dpUsedForSonification()) {
+ mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
+ }
+ if (mDuplicatedOutput != 0 ||
+ !a2dpUsedForSonification()) {
+ // If both A2DP and duplicated outputs are open, send device address to A2DP hardware
+ // interface
+ AudioParameter param;
+ param.add(String8("a2dp_sink_address"), String8(device_address));
+ mpClientInterface->setParameters(mA2dpOutput, param.toString());
+ mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+
+ if (a2dpUsedForSonification()) {
+ // add duplicated output descriptor
+ AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
+ dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
+ dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
+ dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
+ dupOutputDesc->mFormat = outputDesc->mFormat;
+ dupOutputDesc->mChannels = outputDesc->mChannels;
+ dupOutputDesc->mLatency = outputDesc->mLatency;
+ addOutput(mDuplicatedOutput, dupOutputDesc);
+ applyStreamVolumes(mDuplicatedOutput, device);
+ }
+ } else {
+ LOGW("getOutput() could not open duplicated output for %d and %d",
+ mHardwareOutput, mA2dpOutput);
+ mpClientInterface->closeOutput(mA2dpOutput);
+ mOutputs.removeItem(mA2dpOutput);
+ mA2dpOutput = 0;
+ delete outputDesc;
+ return NO_INIT;
+ }
+ } else {
+ LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
+ delete outputDesc;
+ return NO_INIT;
+ }
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+ if (mScoDeviceAddress != "") {
+ // It is normal to suspend twice if we are both in call,
+ // and have the hardware audio output routed to BT SCO
+ if (mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ }
+
+ if (!a2dpUsedForSonification()) {
+ // mute music on A2DP output if a notification or ringtone is playing
+ uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
+ for (uint32_t i = 0; i < refCount; i++) {
+ setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ if (mA2dpOutput == 0) {
+ LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
+ return INVALID_OPERATION;
+ }
+
+ if (mA2dpDeviceAddress != device_address) {
+ LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
+ return INVALID_OPERATION;
+ }
+
+ // mute media strategy to avoid outputting sound on hardware output while music stream
+ // is switched from A2DP output and before music is paused by music application
+ setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
+ setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, MUTE_TIME_MS);
+
+ if (!a2dpUsedForSonification()) {
+ // unmute music on A2DP output if a notification or ringtone is playing
+ uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
+ for (uint32_t i = 0; i < refCount; i++) {
+ setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
+ }
+ }
+ mA2dpDeviceAddress = "";
+ return NO_ERROR;
+}
+
+void AudioPolicyManagerBase::closeA2dpOutputs()
+{
+ LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
+
+ if (mDuplicatedOutput != 0) {
+ mpClientInterface->closeOutput(mDuplicatedOutput);
+ delete mOutputs.valueFor(mDuplicatedOutput);
+ mOutputs.removeItem(mDuplicatedOutput);
+ mDuplicatedOutput = 0;
+ }
+ if (mA2dpOutput != 0) {
+ AudioParameter param;
+ param.add(String8("closing"), String8("true"));
+ mpClientInterface->setParameters(mA2dpOutput, param.toString());
+ mpClientInterface->closeOutput(mA2dpOutput);
+ delete mOutputs.valueFor(mA2dpOutput);
+ mOutputs.removeItem(mA2dpOutput);
+ mA2dpOutput = 0;
+ }
+}
+
+void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, uint32_t &newDevice)
+{
+ uint32_t prevDevice = getDeviceForStrategy(strategy);
+ uint32_t curDevice = getDeviceForStrategy(strategy, false);
+ bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+ bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+ AudioOutputDescriptor *a2dpOutputDesc;
+
+ if (a2dpWasUsed && !a2dpIsUsed) {
+ bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
+
+ if (dupUsed) {
+ LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
+ a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+ } else {
+ LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
+ a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
+ }
+
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ int refCount = a2dpOutputDesc->mRefCount[i];
+ // in the case of duplicated output, the ref count is first incremented
+ // and then decremented on hardware output tus keeping its value
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
+ a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+ }
+ // do not change newDevice if it was already set before this call by a previous call to
+ // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority
+ if (newDevice == 0 && hwOutputDesc->isUsedByStrategy(strategy)) {
+ newDevice = getDeviceForStrategy(strategy, false);
+ }
+ }
+ if (a2dpIsUsed && !a2dpWasUsed) {
+ bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
+ audio_io_handle_t a2dpOutput;
+
+ if (dupUsed) {
+ LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
+ a2dpOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+ a2dpOutput = mDuplicatedOutput;
+ } else {
+ LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
+ a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
+ a2dpOutput = mA2dpOutput;
+ }
+
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, a2dpOutput);
+ int refCount = hwOutputDesc->mRefCount[i];
+ // in the case of duplicated output, the ref count is first incremented
+ // and then decremented on hardware output tus keeping its value
+ a2dpOutputDesc->changeRefCount((AudioSystem::stream_type)i, refCount);
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+ }
+ }
+}
+
+void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice)
+{
+ // Check strategies in order of priority so that once newDevice is set
+ // for a given strategy it is not modified by subsequent calls to
+ // checkOutputForStrategy()
+ checkOutputForStrategy(STRATEGY_PHONE, newDevice);
+ checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice);
+ checkOutputForStrategy(STRATEGY_MEDIA, newDevice);
+ checkOutputForStrategy(STRATEGY_DTMF, newDevice);
+}
+
+#endif
+
+uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
+{
+ uint32_t device = 0;
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ // check the following by order of priority to request a routing change if necessary:
+ // 1: we are in call or the strategy phone is active on the hardware output:
+ // use device for strategy phone
+ // 2: the strategy sonification is active on the hardware output:
+ // use device for strategy sonification
+ // 3: the strategy media is active on the hardware output:
+ // use device for strategy media
+ // 4: the strategy DTMF is active on the hardware output:
+ // use device for strategy DTMF
+ if (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
+ device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+ device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
+ device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
+ }
+
+ LOGV("getNewDevice() selected device %x", device);
+ return device;
+}
+
+AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(AudioSystem::stream_type stream)
+{
+ // stream to strategy mapping
+ switch (stream) {
+ case AudioSystem::VOICE_CALL:
+ case AudioSystem::BLUETOOTH_SCO:
+ return STRATEGY_PHONE;
+ case AudioSystem::RING:
+ case AudioSystem::NOTIFICATION:
+ case AudioSystem::ALARM:
+ case AudioSystem::ENFORCED_AUDIBLE:
+ return STRATEGY_SONIFICATION;
+ case AudioSystem::DTMF:
+ return STRATEGY_DTMF;
+ default:
+ LOGE("unknown stream type");
+ case AudioSystem::SYSTEM:
+ // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
+ // while key clicks are played produces a poor result
+ case AudioSystem::TTS:
+ case AudioSystem::MUSIC:
+ return STRATEGY_MEDIA;
+ }
+}
+
+uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
+{
+ uint32_t device = 0;
+
+ if (fromCache) {
+ LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
+ return mDeviceForStrategy[strategy];
+ }
+
+ switch (strategy) {
+ case STRATEGY_DTMF:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ // when off call, DTMF strategy follows the same rules as MEDIA strategy
+ device = getDeviceForStrategy(STRATEGY_MEDIA, false);
+ break;
+ }
+ // when in call, DTMF and PHONE strategies follow the same rules
+ // FALL THROUGH
+
+ case STRATEGY_PHONE:
+ // for phone strategy, we first consider the forced use and then the available devices by order
+ // of priority
+ switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
+ case AudioSystem::FORCE_BT_SCO:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ if (device) break;
+ }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
+ if (device) break;
+ // if SCO device is requested but no SCO device is available, fall back to default case
+ // FALL THROUGH
+
+ default: // FORCE_NONE
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ if (device) break;
+#ifdef WITH_A2DP
+ // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+ if (device) break;
+ }
+#endif
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() earpiece device not found");
+ }
+ break;
+
+ case AudioSystem::FORCE_SPEAKER:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ if (device) break;
+ }
+#ifdef WITH_A2DP
+ // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
+ // A2DP speaker when forcing to speaker output
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+ if (device) break;
+ }
+#endif
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ break;
+ }
+ break;
+
+ case STRATEGY_SONIFICATION:
+
+ // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
+ // handleIncallSonification().
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ device = getDeviceForStrategy(STRATEGY_PHONE, false);
+ break;
+ }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ // The second device used for sonification is the same as the device used by media strategy
+ // FALL THROUGH
+
+ case STRATEGY_MEDIA: {
+ uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ }
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0) {
+ if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
+ break;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+ }
+ }
+#endif
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ }
+
+ // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
+ device |= device2;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ } break;
+
+ default:
+ LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
+ break;
+ }
+
+ LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
+ return device;
+}
+
+void AudioPolicyManagerBase::updateDeviceForStrategy()
+{
+ for (int i = 0; i < NUM_STRATEGIES; i++) {
+ mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
+ }
+}
+
+void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
+{
+ LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+
+ if (outputDesc->isDuplicated()) {
+ setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
+ setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
+ return;
+ }
+#ifdef WITH_A2DP
+ // filter devices according to output selected
+ if (output == mA2dpOutput) {
+ device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
+ } else {
+ device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
+ }
+#endif
+
+ uint32_t prevDevice = (uint32_t)outputDesc->device();
+ // Do not change the routing if:
+ // - the requestede device is 0
+ // - the requested device is the same as current device and force is not specified.
+ // Doing this check here allows the caller to call setOutputDevice() without conditions
+ if ((device == 0 || device == prevDevice) && !force) {
+ LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
+ return;
+ }
+
+ outputDesc->mDevice = device;
+ // mute media streams if both speaker and headset are selected
+ if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
+ setStrategyMute(STRATEGY_MEDIA, true, output);
+ // wait for the PCM output buffers to empty before proceeding with the rest of the command
+ usleep(outputDesc->mLatency*2*1000);
+ }
+#ifdef WITH_A2DP
+ // suspend A2DP output if SCO device is selected
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
+ if (mA2dpOutput != 0) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ }
+#endif
+ // do the routing
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)device);
+ mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
+ // update stream volumes according to new device
+ applyStreamVolumes(output, device, delayMs);
+
+#ifdef WITH_A2DP
+ // if disconnecting SCO device, restore A2DP output
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
+ if (mA2dpOutput != 0) {
+ LOGV("restore A2DP output");
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+#endif
+ // if changing from a combined headset + speaker route, unmute media streams
+ if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
+ setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
+ }
+}
+
+uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
+{
+ uint32_t device;
+
+ switch(inputSource) {
+ case AUDIO_SOURCE_DEFAULT:
+ case AUDIO_SOURCE_MIC:
+ case AUDIO_SOURCE_VOICE_RECOGNITION:
+ if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
+ mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
+ device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
+ device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+ } else {
+ device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+ }
+ break;
+ case AUDIO_SOURCE_CAMCORDER:
+ if (hasBackMicrophone()) {
+ device = AudioSystem::DEVICE_IN_BACK_MIC;
+ } else {
+ device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+ }
+ break;
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ case AUDIO_SOURCE_VOICE_CALL:
+ device = AudioSystem::DEVICE_IN_VOICE_CALL;
+ break;
+ default:
+ LOGW("getInput() invalid input source %d", inputSource);
+ device = 0;
+ break;
+ }
+ LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
+ return device;
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
+{
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ if (mInputs.valueAt(i)->mRefCount > 0) {
+ return mInputs.keyAt(i);
+ }
+ }
+ return 0;
+}
+
+float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
+{
+ float volume = 1.0;
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ StreamDescriptor &streamDesc = mStreams[stream];
+
+ if (device == 0) {
+ device = outputDesc->device();
+ }
+
+ int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
+ volume = AudioSystem::linearToLog(volInt);
+
+ // if a headset is connected, apply the following rules to ring tones and notifications
+ // to avoid sound level bursts in user's ears:
+ // - always attenuate ring tones and notifications volume by 6dB
+ // - if music is playing, always limit the volume to current music volume,
+ // with a minimum threshold at -36dB so that notification is always perceived.
+ if ((device &
+ (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AudioSystem::DEVICE_OUT_WIRED_HEADSET |
+ AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
+ (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) &&
+ streamDesc.mCanBeMuted) {
+ volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+ // when the phone is ringing we must consider that music could have been paused just before
+ // by the music application and behave as if music was active if the last music track was
+ // just stopped
+ if (outputDesc->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
+ float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
+ float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
+ if (volume > minVol) {
+ volume = minVol;
+ LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+ }
+ }
+ }
+
+ return volume;
+}
+
+status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
+{
+
+ // do not change actual stream volume if the stream is muted
+ if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+ LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+ return NO_ERROR;
+ }
+
+ // do not change in call volume if bluetooth is connected and vice versa
+ if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+ (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
+ LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
+ stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
+ return INVALID_OPERATION;
+ }
+
+ float volume = computeVolume(stream, index, output, device);
+ // do not set volume if the float value did not change
+ if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+ mOutputs.valueFor(output)->mCurVolume[stream] = volume;
+ LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
+ if (stream == AudioSystem::VOICE_CALL ||
+ stream == AudioSystem::DTMF ||
+ stream == AudioSystem::BLUETOOTH_SCO) {
+ float voiceVolume = -1.0;
+ // offset value to reflect actual hardware volume that never reaches 0
+ // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
+ volume = 0.01 + 0.99 * volume;
+ if (stream == AudioSystem::VOICE_CALL) {
+ voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
+ } else if (stream == AudioSystem::BLUETOOTH_SCO) {
+ voiceVolume = 1.0;
+ }
+ if (voiceVolume >= 0 && output == mHardwareOutput) {
+ mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+ }
+ }
+ mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
+ }
+
+ return NO_ERROR;
+}
+
+void AudioPolicyManagerBase::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
+{
+ LOGV("applyStreamVolumes() for output %d and device %x", output, device);
+
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
+ }
+}
+
+void AudioPolicyManagerBase::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
+{
+ LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
+ setStreamMute(stream, on, output, delayMs);
+ }
+ }
+}
+
+void AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
+{
+ StreamDescriptor &streamDesc = mStreams[stream];
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+ LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
+
+ if (on) {
+ if (outputDesc->mMuteCount[stream] == 0) {
+ if (streamDesc.mCanBeMuted) {
+ checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
+ }
+ }
+ // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
+ outputDesc->mMuteCount[stream]++;
+ } else {
+ if (outputDesc->mMuteCount[stream] == 0) {
+ LOGW("setStreamMute() unmuting non muted stream!");
+ return;
+ }
+ if (--outputDesc->mMuteCount[stream] == 0) {
+ checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
+ }
+ }
+}
+
+void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, bool stateChange)
+{
+ // if the stream pertains to sonification strategy and we are in call we must
+ // mute the stream if it is low visibility. If it is high visibility, we must play a tone
+ // in the device used for phone strategy and play the tone if the selected device does not
+ // interfere with the device used for phone strategy
+ // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as
+ // many times as there are active tracks on the output
+
+ if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
+ LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
+ stream, starting, outputDesc->mDevice, stateChange);
+ if (outputDesc->mRefCount[stream]) {
+ int muteCount = 1;
+ if (stateChange) {
+ muteCount = outputDesc->mRefCount[stream];
+ }
+ if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
+ LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
+ for (int i = 0; i < muteCount; i++) {
+ setStreamMute(stream, starting, mHardwareOutput);
+ }
+ } else {
+ LOGV("handleIncallSonification() high visibility");
+ if (outputDesc->device() & getDeviceForStrategy(STRATEGY_PHONE)) {
+ LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
+ for (int i = 0; i < muteCount; i++) {
+ setStreamMute(stream, starting, mHardwareOutput);
+ }
+ }
+ if (starting) {
+ mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
+ } else {
+ mpClientInterface->stopTone();
+ }
+ }
+ }
+ }
+}
+
+bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags,
+ uint32_t device)
+{
+ return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format !=0 && !AudioSystem::isLinearPCM(format)));
+}
+
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
+ : mId(0), mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
+ mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
+{
+ // clear usage count for all stream types
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ mRefCount[i] = 0;
+ mCurVolume[i] = -1.0;
+ mMuteCount[i] = 0;
+ }
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device()
+{
+ uint32_t device = 0;
+ if (isDuplicated()) {
+ device = mOutput1->mDevice | mOutput2->mDevice;
+ } else {
+ device = mDevice;
+ }
+ return device;
+}
+
+void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
+{
+ // forward usage count change to attached outputs
+ if (isDuplicated()) {
+ mOutput1->changeRefCount(stream, delta);
+ mOutput2->changeRefCount(stream, delta);
+ }
+ if ((delta + (int)mRefCount[stream]) < 0) {
+ LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
+ mRefCount[stream] = 0;
+ return;
+ }
+ mRefCount[stream] += delta;
+ LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
+{
+ uint32_t refcount = 0;
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ refcount += mRefCount[i];
+ }
+ return refcount;
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy)
+{
+ uint32_t refCount = 0;
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+ refCount += mRefCount[i];
+ }
+ }
+ return refCount;
+}
+
+
+status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Devices %08x\n", device());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
+ result.append(buffer);
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
+ result.append(buffer);
+ }
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+// --- AudioInputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0),
+ mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+status_t AudioPolicyManagerBase::AudioInputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+// --- StreamDescriptor class implementation
+
+void AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %02d %02d %02d %d\n",
+ mIndexMin,
+ mIndexMax,
+ mIndexCur,
+ mCanBeMuted);
+}
+
+
+}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
new file mode 100644
index 0000000..bb3905c
--- /dev/null
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioPolicyService"
+//#define LOG_NDEBUG 0
+
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include <sys/time.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <cutils/properties.h>
+#include <binder/IPCThreadState.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+#include "AudioPolicyService.h"
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
+#include <hardware_legacy/power.h>
+
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+namespace android {
+
+
+static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
+static const char *kCmdDeadlockedString = "AudioPolicyService command thread may be deadlocked\n";
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
+static bool checkPermission() {
+#ifndef HAVE_ANDROID_OS
+ return true;
+#endif
+ if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+ bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
+ if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
+ return ok;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioPolicyService::AudioPolicyService()
+ : BnAudioPolicyService() , mpPolicyManager(NULL)
+{
+ char value[PROPERTY_VALUE_MAX];
+
+ // start tone playback thread
+ mTonePlaybackThread = new AudioCommandThread(String8(""));
+ // start audio commands thread
+ mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));
+
+#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
+ mpPolicyManager = new AudioPolicyManagerBase(this);
+ LOGV("build for GENERIC_AUDIO - using generic audio policy");
+#else
+ // if running in emulation - use the emulator driver
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ LOGV("Running in emulation - using generic audio policy");
+ mpPolicyManager = new AudioPolicyManagerBase(this);
+ }
+ else {
+ LOGV("Using hardware specific audio policy");
+ mpPolicyManager = createAudioPolicyManager(this);
+ }
+#endif
+
+ // load properties
+ property_get("ro.camera.sound.forced", value, "0");
+ mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
+}
+
+AudioPolicyService::~AudioPolicyService()
+{
+ mTonePlaybackThread->exit();
+ mTonePlaybackThread.clear();
+ mAudioCommandThread->exit();
+ mAudioCommandThread.clear();
+
+ if (mpPolicyManager) {
+ delete mpPolicyManager;
+ }
+}
+
+
+status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
+ return BAD_VALUE;
+ }
+ if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setDeviceConnectionState() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
+}
+
+AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ if (mpPolicyManager == NULL) {
+ return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ }
+ if (!checkPermission()) {
+ return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ }
+ return mpPolicyManager->getDeviceConnectionState(device, device_address);
+}
+
+status_t AudioPolicyService::setPhoneState(int state)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setPhoneState() tid %d", gettid());
+
+ // TODO: check if it is more appropriate to do it in platform specific policy manager
+ AudioSystem::setMode(state);
+
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->setPhoneState(state);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+
+ mpPolicyManager->setRingerMode(mode, mask);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ return BAD_VALUE;
+ }
+ if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) {
+ return BAD_VALUE;
+ }
+ LOGV("setForceUse() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->setForceUse(usage, config);
+ return NO_ERROR;
+}
+
+AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage)
+{
+ if (mpPolicyManager == NULL) {
+ return AudioSystem::FORCE_NONE;
+ }
+ if (!checkPermission()) {
+ return AudioSystem::FORCE_NONE;
+ }
+ if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ return AudioSystem::FORCE_NONE;
+ }
+ return mpPolicyManager->getForceUse(usage);
+}
+
+audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+{
+ if (mpPolicyManager == NULL) {
+ return 0;
+ }
+ LOGV("getOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
+}
+
+status_t AudioPolicyService::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ LOGV("startOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->startOutput(output, stream);
+}
+
+status_t AudioPolicyService::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ LOGV("stopOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->stopOutput(output, stream);
+}
+
+void AudioPolicyService::releaseOutput(audio_io_handle_t output)
+{
+ if (mpPolicyManager == NULL) {
+ return;
+ }
+ LOGV("releaseOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->releaseOutput(output);
+}
+
+audio_io_handle_t AudioPolicyService::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ if (mpPolicyManager == NULL) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics);
+}
+
+status_t AudioPolicyService::startInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->startInput(input);
+}
+
+status_t AudioPolicyService::stopInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->stopInput(input);
+}
+
+void AudioPolicyService::releaseInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return;
+ }
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->releaseInput(input);
+}
+
+status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+ mpPolicyManager->initStreamVolume(stream, indexMin, indexMax);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+
+ return mpPolicyManager->setStreamVolumeIndex(stream, index);
+}
+
+status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+ return mpPolicyManager->getStreamVolumeIndex(stream, index);
+}
+
+void AudioPolicyService::binderDied(const wp<IBinder>& who) {
+ LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
+}
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t AudioPolicyService::dumpInternals(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpPolicyManager);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get());
+ result.append(buffer);
+
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
+{
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ dumpPermissionDenial(fd);
+ } else {
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ String8 result(kDeadlockedString);
+ write(fd, result.string(), result.size());
+ }
+
+ dumpInternals(fd);
+ if (mAudioCommandThread != NULL) {
+ mAudioCommandThread->dump(fd);
+ }
+ if (mTonePlaybackThread != NULL) {
+ mTonePlaybackThread->dump(fd);
+ }
+
+ if (mpPolicyManager) {
+ mpPolicyManager->dump(fd);
+ }
+
+ if (locked) mLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::dumpPermissionDenial(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump AudioPolicyService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioPolicyService::onTransact(code, data, reply, flags);
+}
+
+
+// ----------------------------------------------------------------------------
+void AudioPolicyService::instantiate() {
+ defaultServiceManager()->addService(
+ String16("media.audio_policy"), new AudioPolicyService());
+}
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyClientInterface implementation
+// ----------------------------------------------------------------------------
+
+
+audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ AudioSystem::output_flags flags)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openOutput() could not get AudioFlinger");
+ return 0;
+ }
+
+ return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags);
+}
+
+audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openDuplicateOutput() could not get AudioFlinger");
+ return 0;
+ }
+ return af->openDuplicateOutput(output1, output2);
+}
+
+status_t AudioPolicyService::closeOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->closeOutput(output);
+}
+
+
+status_t AudioPolicyService::suspendOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("suspendOutput() could not get AudioFlinger");
+ return PERMISSION_DENIED;
+ }
+
+ return af->suspendOutput(output);
+}
+
+status_t AudioPolicyService::restoreOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("restoreOutput() could not get AudioFlinger");
+ return PERMISSION_DENIED;
+ }
+
+ return af->restoreOutput(output);
+}
+
+audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openInput() could not get AudioFlinger");
+ return 0;
+ }
+
+ return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics);
+}
+
+status_t AudioPolicyService::closeInput(audio_io_handle_t input)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->closeInput(input);
+}
+
+status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs)
+{
+ return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
+}
+
+status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->setStreamOutput(stream, output);
+}
+
+
+void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs)
+{
+ mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
+}
+
+String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys)
+{
+ String8 result = AudioSystem::getParameters(ioHandle, keys);
+ return result;
+}
+
+status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream)
+{
+ mTonePlaybackThread->startToneCommand(tone, stream);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::stopTone()
+{
+ mTonePlaybackThread->stopToneCommand();
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setVoiceVolume(float volume, int delayMs)
+{
+ return mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
+}
+
+// ----------- AudioPolicyService::AudioCommandThread implementation ----------
+
+AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
+ : Thread(false), mName(name)
+{
+ mpToneGenerator = NULL;
+}
+
+
+AudioPolicyService::AudioCommandThread::~AudioCommandThread()
+{
+ if (mName != "" && !mAudioCommands.isEmpty()) {
+ release_wake_lock(mName.string());
+ }
+ mAudioCommands.clear();
+ if (mpToneGenerator != NULL) delete mpToneGenerator;
+}
+
+void AudioPolicyService::AudioCommandThread::onFirstRef()
+{
+ if (mName != "") {
+ run(mName.string(), ANDROID_PRIORITY_AUDIO);
+ } else {
+ run("AudioCommandThread", ANDROID_PRIORITY_AUDIO);
+ }
+}
+
+bool AudioPolicyService::AudioCommandThread::threadLoop()
+{
+ nsecs_t waitTime = INT64_MAX;
+
+ mLock.lock();
+ while (!exitPending())
+ {
+ while(!mAudioCommands.isEmpty()) {
+ nsecs_t curTime = systemTime();
+ // commands are sorted by increasing time stamp: execute them from index 0 and up
+ if (mAudioCommands[0]->mTime <= curTime) {
+ AudioCommand *command = mAudioCommands[0];
+ mAudioCommands.removeAt(0);
+ mLastCommand = *command;
+
+ switch (command->mCommand) {
+ case START_TONE: {
+ mLock.unlock();
+ ToneData *data = (ToneData *)command->mParam;
+ LOGV("AudioCommandThread() processing start tone %d on stream %d",
+ data->mType, data->mStream);
+ if (mpToneGenerator != NULL)
+ delete mpToneGenerator;
+ mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
+ mpToneGenerator->startTone(data->mType);
+ delete data;
+ mLock.lock();
+ }break;
+ case STOP_TONE: {
+ mLock.unlock();
+ LOGV("AudioCommandThread() processing stop tone");
+ if (mpToneGenerator != NULL) {
+ mpToneGenerator->stopTone();
+ delete mpToneGenerator;
+ mpToneGenerator = NULL;
+ }
+ mLock.lock();
+ }break;
+ case SET_VOLUME: {
+ VolumeData *data = (VolumeData *)command->mParam;
+ LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %d", data->mStream, data->mVolume, data->mIO);
+ command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO);
+ if (command->mWaitStatus) {
+ command->mCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ delete data;
+ }break;
+ case SET_PARAMETERS: {
+ ParametersData *data = (ParametersData *)command->mParam;
+ LOGV("AudioCommandThread() processing set parameters string %s, io %d", data->mKeyValuePairs.string(), data->mIO);
+ command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
+ if (command->mWaitStatus) {
+ command->mCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ delete data;
+ }break;
+ case SET_VOICE_VOLUME: {
+ VoiceVolumeData *data = (VoiceVolumeData *)command->mParam;
+ LOGV("AudioCommandThread() processing set voice volume volume %f", data->mVolume);
+ command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
+ if (command->mWaitStatus) {
+ command->mCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ delete data;
+ }break;
+ default:
+ LOGW("AudioCommandThread() unknown command %d", command->mCommand);
+ }
+ delete command;
+ waitTime = INT64_MAX;
+ } else {
+ waitTime = mAudioCommands[0]->mTime - curTime;
+ break;
+ }
+ }
+ // release delayed commands wake lock
+ if (mName != "" && mAudioCommands.isEmpty()) {
+ release_wake_lock(mName.string());
+ }
+ LOGV("AudioCommandThread() going to sleep");
+ mWaitWorkCV.waitRelative(mLock, waitTime);
+ LOGV("AudioCommandThread() waking up");
+ }
+ mLock.unlock();
+ return false;
+}
+
+status_t AudioPolicyService::AudioCommandThread::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "AudioCommandThread %p Dump\n", this);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ String8 result2(kCmdDeadlockedString);
+ write(fd, result2.string(), result2.size());
+ }
+
+ snprintf(buffer, SIZE, "- Commands:\n");
+ result = String8(buffer);
+ result.append(" Command Time Wait pParam\n");
+ for (int i = 0; i < (int)mAudioCommands.size(); i++) {
+ mAudioCommands[i]->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ result.append(" Last Command\n");
+ mLastCommand.dump(buffer, SIZE);
+ result.append(buffer);
+
+ write(fd, result.string(), result.size());
+
+ if (locked) mLock.unlock();
+
+ return NO_ERROR;
+}
+
+void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stream)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = START_TONE;
+ ToneData *data = new ToneData();
+ data->mType = type;
+ data->mStream = stream;
+ command->mParam = (void *)data;
+ command->mWaitStatus = false;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ LOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream);
+ mWaitWorkCV.signal();
+}
+
+void AudioPolicyService::AudioCommandThread::stopToneCommand()
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = STOP_TONE;
+ command->mParam = NULL;
+ command->mWaitStatus = false;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ LOGV("AudioCommandThread() adding tone stop");
+ mWaitWorkCV.signal();
+}
+
+status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output, int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = SET_VOLUME;
+ VolumeData *data = new VolumeData();
+ data->mStream = stream;
+ data->mVolume = volume;
+ data->mIO = output;
+ command->mParam = data;
+ if (delayMs == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command, delayMs);
+ LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", stream, volume, output);
+ mWaitWorkCV.signal();
+ if (command->mWaitStatus) {
+ command->mCond.wait(mLock);
+ status = command->mStatus;
+ mWaitWorkCV.signal();
+ }
+ return status;
+}
+
+status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = SET_PARAMETERS;
+ ParametersData *data = new ParametersData();
+ data->mIO = ioHandle;
+ data->mKeyValuePairs = keyValuePairs;
+ command->mParam = data;
+ if (delayMs == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command, delayMs);
+ LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", keyValuePairs.string(), ioHandle, delayMs);
+ mWaitWorkCV.signal();
+ if (command->mWaitStatus) {
+ command->mCond.wait(mLock);
+ status = command->mStatus;
+ mWaitWorkCV.signal();
+ }
+ return status;
+}
+
+status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = SET_VOICE_VOLUME;
+ VoiceVolumeData *data = new VoiceVolumeData();
+ data->mVolume = volume;
+ command->mParam = data;
+ if (delayMs == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command, delayMs);
+ LOGV("AudioCommandThread() adding set voice volume volume %f", volume);
+ mWaitWorkCV.signal();
+ if (command->mWaitStatus) {
+ command->mCond.wait(mLock);
+ status = command->mStatus;
+ mWaitWorkCV.signal();
+ }
+ return status;
+}
+
+// insertCommand_l() must be called with mLock held
+void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
+{
+ ssize_t i;
+ Vector <AudioCommand *> removedCommands;
+
+ command->mTime = systemTime() + milliseconds(delayMs);
+
+ // acquire wake lock to make sure delayed commands are processed
+ if (mName != "" && mAudioCommands.isEmpty()) {
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
+ }
+
+ // check same pending commands with later time stamps and eliminate them
+ for (i = mAudioCommands.size()-1; i >= 0; i--) {
+ AudioCommand *command2 = mAudioCommands[i];
+ // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
+ if (command2->mTime <= command->mTime) break;
+ if (command2->mCommand != command->mCommand) continue;
+
+ switch (command->mCommand) {
+ case SET_PARAMETERS: {
+ ParametersData *data = (ParametersData *)command->mParam;
+ ParametersData *data2 = (ParametersData *)command2->mParam;
+ if (data->mIO != data2->mIO) break;
+ LOGV("Comparing parameter command %s to new command %s", data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
+ AudioParameter param = AudioParameter(data->mKeyValuePairs);
+ AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
+ for (size_t j = 0; j < param.size(); j++) {
+ String8 key;
+ String8 value;
+ param.getAt(j, key, value);
+ for (size_t k = 0; k < param2.size(); k++) {
+ String8 key2;
+ String8 value2;
+ param2.getAt(k, key2, value2);
+ if (key2 == key) {
+ param2.remove(key2);
+ LOGV("Filtering out parameter %s", key2.string());
+ break;
+ }
+ }
+ }
+ // if all keys have been filtered out, remove the command.
+ // otherwise, update the key value pairs
+ if (param2.size() == 0) {
+ removedCommands.add(command2);
+ } else {
+ data2->mKeyValuePairs = param2.toString();
+ }
+ } break;
+
+ case SET_VOLUME: {
+ VolumeData *data = (VolumeData *)command->mParam;
+ VolumeData *data2 = (VolumeData *)command2->mParam;
+ if (data->mIO != data2->mIO) break;
+ if (data->mStream != data2->mStream) break;
+ LOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream);
+ removedCommands.add(command2);
+ } break;
+ case START_TONE:
+ case STOP_TONE:
+ default:
+ break;
+ }
+ }
+
+ // remove filtered commands
+ for (size_t j = 0; j < removedCommands.size(); j++) {
+ // removed commands always have time stamps greater than current command
+ for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
+ if (mAudioCommands[k] == removedCommands[j]) {
+ LOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
+ mAudioCommands.removeAt(k);
+ break;
+ }
+ }
+ }
+ removedCommands.clear();
+
+ // insert command at the right place according to its time stamp
+ LOGV("inserting command: %d at index %d, num commands %d", command->mCommand, (int)i+1, mAudioCommands.size());
+ mAudioCommands.insertAt(command, i + 1);
+}
+
+void AudioPolicyService::AudioCommandThread::exit()
+{
+ LOGV("AudioCommandThread::exit");
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %02d %06d.%03d %01u %p\n",
+ mCommand,
+ (int)ns2s(mTime),
+ (int)ns2ms(mTime)%1000,
+ mWaitStatus,
+ mParam);
+}
+
+}; // namespace android
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
new file mode 100644
index 0000000..a13d0bd
--- /dev/null
+++ b/services/audioflinger/AudioPolicyService.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIOPOLICYSERVICE_H
+#define ANDROID_AUDIOPOLICYSERVICE_H
+
+#include <media/IAudioPolicyService.h>
+#include <hardware_legacy/AudioPolicyInterface.h>
+#include <media/ToneGenerator.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class String8;
+
+// ----------------------------------------------------------------------------
+
+class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, public IBinder::DeathRecipient
+{
+
+public:
+ static void instantiate();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ //
+ // BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
+ //
+
+ virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address);
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address);
+ virtual status_t setPhoneState(int state);
+ virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
+ virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
+ virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT);
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual void releaseOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0);
+ virtual status_t startInput(audio_io_handle_t input);
+ virtual status_t stopInput(audio_io_handle_t input);
+ virtual void releaseInput(audio_io_handle_t input);
+ virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax);
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags);
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ //
+ // AudioPolicyClientInterface
+ //
+ virtual audio_io_handle_t openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ AudioSystem::output_flags flags);
+ virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2);
+ virtual status_t closeOutput(audio_io_handle_t output);
+ virtual status_t suspendOutput(audio_io_handle_t output);
+ virtual status_t restoreOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics);
+ virtual status_t closeInput(audio_io_handle_t input);
+ virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0);
+ virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output);
+ virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
+ virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream);
+ virtual status_t stopTone();
+ virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+
+private:
+ AudioPolicyService();
+ virtual ~AudioPolicyService();
+
+ status_t dumpInternals(int fd);
+
+ // Thread used for tone playback and to send audio config commands to audio flinger
+ // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because startTone()
+ // and stopTone() are normally called with mLock locked and requesting a tone start or stop will cause
+ // calls to AudioPolicyService and an attempt to lock mLock.
+ // For audio config commands, it is necessary because audio flinger requires that the calling process (user)
+ // has permission to modify audio settings.
+ class AudioCommandThread : public Thread {
+ class AudioCommand;
+ public:
+
+ // commands for tone AudioCommand
+ enum {
+ START_TONE,
+ STOP_TONE,
+ SET_VOLUME,
+ SET_PARAMETERS,
+ SET_VOICE_VOLUME
+ };
+
+ AudioCommandThread (String8 name);
+ virtual ~AudioCommandThread();
+
+ status_t dump(int fd);
+
+ // Thread virtuals
+ virtual void onFirstRef();
+ virtual bool threadLoop();
+
+ void exit();
+ void startToneCommand(int type = 0, int stream = 0);
+ void stopToneCommand();
+ status_t volumeCommand(int stream, float volume, int output, int delayMs = 0);
+ status_t parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ status_t voiceVolumeCommand(float volume, int delayMs = 0);
+ void insertCommand_l(AudioCommand *command, int delayMs = 0);
+
+ private:
+ // descriptor for requested tone playback event
+ class AudioCommand {
+
+ public:
+ AudioCommand()
+ : mCommand(-1) {}
+
+ void dump(char* buffer, size_t size);
+
+ int mCommand; // START_TONE, STOP_TONE ...
+ nsecs_t mTime; // time stamp
+ Condition mCond; // condition for status return
+ status_t mStatus; // command status
+ bool mWaitStatus; // true if caller is waiting for status
+ void *mParam; // command parameter (ToneData, VolumeData, ParametersData)
+ };
+
+ class ToneData {
+ public:
+ int mType; // tone type (START_TONE only)
+ int mStream; // stream type (START_TONE only)
+ };
+
+ class VolumeData {
+ public:
+ int mStream;
+ float mVolume;
+ int mIO;
+ };
+
+ class ParametersData {
+ public:
+ int mIO;
+ String8 mKeyValuePairs;
+ };
+
+ class VoiceVolumeData {
+ public:
+ float mVolume;
+ };
+
+ Mutex mLock;
+ Condition mWaitWorkCV;
+ Vector <AudioCommand *> mAudioCommands; // list of pending commands
+ ToneGenerator *mpToneGenerator; // the tone generator
+ AudioCommand mLastCommand; // last processed command (used by dump)
+ String8 mName; // string used by wake lock fo delayed commands
+ };
+
+ // Internal dump utilities.
+ status_t dumpPermissionDenial(int fd);
+
+
+ Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing device
+ // connection stated our routing
+ AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager
+ sp <AudioCommandThread> mAudioCommandThread; // audio commands thread
+ sp <AudioCommandThread> mTonePlaybackThread; // tone playback thread
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOPOLICYSERVICE_H
+
+
+
+
+
+
+
+
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
new file mode 100644
index 0000000..5dabacb
--- /dev/null
+++ b/services/audioflinger/AudioResampler.cpp
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioResampler"
+//#define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "AudioResampler.h"
+#include "AudioResamplerSinc.h"
+#include "AudioResamplerCubic.h"
+
+namespace android {
+
+#ifdef __ARM_ARCH_5E__ // optimized asm option
+ #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1
+#endif // __ARM_ARCH_5E__
+// ----------------------------------------------------------------------------
+
+class AudioResamplerOrder1 : public AudioResampler {
+public:
+ AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) :
+ AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) {
+ }
+ virtual void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+private:
+ // number of bits used in interpolation multiply - 15 bits avoids overflow
+ static const int kNumInterpBits = 15;
+
+ // bits to shift the phase fraction down to avoid overflow
+ static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
+
+ void init() {}
+ void resampleMono16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+ void resampleStereo16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
+ void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
+ size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
+ uint32_t &phaseFraction, uint32_t phaseIncrement);
+ void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
+ size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
+ uint32_t &phaseFraction, uint32_t phaseIncrement);
+#endif // ASM_ARM_RESAMP1
+
+ static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) {
+ return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits);
+ }
+ static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) {
+ *frac += inc;
+ *index += (size_t)(*frac >> kNumPhaseBits);
+ *frac &= kPhaseMask;
+ }
+ int mX0L;
+ int mX0R;
+};
+
+// ----------------------------------------------------------------------------
+AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
+ int32_t sampleRate, int quality) {
+
+ // can only create low quality resample now
+ AudioResampler* resampler;
+
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("af.resampler.quality", value, 0)) {
+ quality = atoi(value);
+ LOGD("forcing AudioResampler quality to %d", quality);
+ }
+
+ if (quality == DEFAULT)
+ quality = LOW_QUALITY;
+
+ switch (quality) {
+ default:
+ case LOW_QUALITY:
+ LOGV("Create linear Resampler");
+ resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
+ break;
+ case MED_QUALITY:
+ LOGV("Create cubic Resampler");
+ resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
+ break;
+ case HIGH_QUALITY:
+ LOGV("Create sinc Resampler");
+ resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
+ break;
+ }
+
+ // initialize resampler
+ resampler->init();
+ return resampler;
+}
+
+AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
+ int32_t sampleRate) :
+ mBitDepth(bitDepth), mChannelCount(inChannelCount),
+ mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
+ mPhaseFraction(0) {
+ // sanity check on format
+ if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
+ LOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
+ inChannelCount);
+ // LOG_ASSERT(0);
+ }
+
+ // initialize common members
+ mVolume[0] = mVolume[1] = 0;
+ mBuffer.frameCount = 0;
+
+ // save format for quick lookup
+ if (inChannelCount == 1) {
+ mFormat = MONO_16_BIT;
+ } else {
+ mFormat = STEREO_16_BIT;
+ }
+}
+
+AudioResampler::~AudioResampler() {
+}
+
+void AudioResampler::setSampleRate(int32_t inSampleRate) {
+ mInSampleRate = inSampleRate;
+ mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
+}
+
+void AudioResampler::setVolume(int16_t left, int16_t right) {
+ // TODO: Implement anti-zipper filter
+ mVolume[0] = left;
+ mVolume[1] = right;
+}
+
+// ----------------------------------------------------------------------------
+
+void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ // should never happen, but we overflow if it does
+ // LOG_ASSERT(outFrameCount < 32767);
+
+ // select the appropriate resampler
+ switch (mChannelCount) {
+ case 1:
+ resampleMono16(out, outFrameCount, provider);
+ break;
+ case 2:
+ resampleStereo16(out, outFrameCount, provider);
+ break;
+ }
+}
+
+void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ int32_t vl = mVolume[0];
+ int32_t vr = mVolume[1];
+
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2;
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
+ // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n",
+ // outFrameCount, inputIndex, phaseFraction, phaseIncrement);
+
+ while (outputIndex < outputSampleCount) {
+
+ // buffer is empty, fetch a new one
+ while (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL) {
+ goto resampleStereo16_exit;
+ }
+
+ // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount);
+ if (mBuffer.frameCount > inputIndex) break;
+
+ inputIndex -= mBuffer.frameCount;
+ mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
+ mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
+ provider->releaseBuffer(&mBuffer);
+ // mBuffer.frameCount == 0 now so we reload a new buffer
+ }
+
+ int16_t *in = mBuffer.i16;
+
+ // handle boundary case
+ while (inputIndex == 0) {
+ // LOGE("boundary case\n");
+ out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction);
+ out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction);
+ Advance(&inputIndex, &phaseFraction, phaseIncrement);
+ if (outputIndex == outputSampleCount)
+ break;
+ }
+
+ // process input samples
+ // LOGE("general case\n");
+
+#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
+ if (inputIndex + 2 < mBuffer.frameCount) {
+ int32_t* maxOutPt;
+ int32_t maxInIdx;
+
+ maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop
+ maxInIdx = mBuffer.frameCount - 2;
+ AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
+ phaseFraction, phaseIncrement);
+ }
+#endif // ASM_ARM_RESAMP1
+
+ while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
+ out[outputIndex++] += vl * Interp(in[inputIndex*2-2],
+ in[inputIndex*2], phaseFraction);
+ out[outputIndex++] += vr * Interp(in[inputIndex*2-1],
+ in[inputIndex*2+1], phaseFraction);
+ Advance(&inputIndex, &phaseFraction, phaseIncrement);
+ }
+
+ // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+
+ // if done with buffer, save samples
+ if (inputIndex >= mBuffer.frameCount) {
+ inputIndex -= mBuffer.frameCount;
+
+ // LOGE("buffer done, new input index %d", inputIndex);
+
+ mX0L = mBuffer.i16[mBuffer.frameCount*2-2];
+ mX0R = mBuffer.i16[mBuffer.frameCount*2-1];
+ provider->releaseBuffer(&mBuffer);
+
+ // verify that the releaseBuffer resets the buffer frameCount
+ // LOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ }
+
+ // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+
+resampleStereo16_exit:
+ // save state
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ int32_t vl = mVolume[0];
+ int32_t vr = mVolume[1];
+
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2;
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
+ // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n",
+ // outFrameCount, inputIndex, phaseFraction, phaseIncrement);
+ while (outputIndex < outputSampleCount) {
+ // buffer is empty, fetch a new one
+ while (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL) {
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+ goto resampleMono16_exit;
+ }
+ // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount);
+ if (mBuffer.frameCount > inputIndex) break;
+
+ inputIndex -= mBuffer.frameCount;
+ mX0L = mBuffer.i16[mBuffer.frameCount-1];
+ provider->releaseBuffer(&mBuffer);
+ // mBuffer.frameCount == 0 now so we reload a new buffer
+ }
+ int16_t *in = mBuffer.i16;
+
+ // handle boundary case
+ while (inputIndex == 0) {
+ // LOGE("boundary case\n");
+ int32_t sample = Interp(mX0L, in[0], phaseFraction);
+ out[outputIndex++] += vl * sample;
+ out[outputIndex++] += vr * sample;
+ Advance(&inputIndex, &phaseFraction, phaseIncrement);
+ if (outputIndex == outputSampleCount)
+ break;
+ }
+
+ // process input samples
+ // LOGE("general case\n");
+
+#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
+ if (inputIndex + 2 < mBuffer.frameCount) {
+ int32_t* maxOutPt;
+ int32_t maxInIdx;
+
+ maxOutPt = out + (outputSampleCount - 2);
+ maxInIdx = (int32_t)mBuffer.frameCount - 2;
+ AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr,
+ phaseFraction, phaseIncrement);
+ }
+#endif // ASM_ARM_RESAMP1
+
+ while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) {
+ int32_t sample = Interp(in[inputIndex-1], in[inputIndex],
+ phaseFraction);
+ out[outputIndex++] += vl * sample;
+ out[outputIndex++] += vr * sample;
+ Advance(&inputIndex, &phaseFraction, phaseIncrement);
+ }
+
+
+ // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+
+ // if done with buffer, save samples
+ if (inputIndex >= mBuffer.frameCount) {
+ inputIndex -= mBuffer.frameCount;
+
+ // LOGE("buffer done, new input index %d", inputIndex);
+
+ mX0L = mBuffer.i16[mBuffer.frameCount-1];
+ provider->releaseBuffer(&mBuffer);
+
+ // verify that the releaseBuffer resets the buffer frameCount
+ // LOG_ASSERT(mBuffer.frameCount == 0);
+ }
+ }
+
+ // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
+
+resampleMono16_exit:
+ // save state
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1
+
+/*******************************************************************
+*
+* AsmMono16Loop
+* asm optimized monotonic loop version; one loop is 2 frames
+* Input:
+* in : pointer on input samples
+* maxOutPt : pointer on first not filled
+* maxInIdx : index on first not used
+* outputIndex : pointer on current output index
+* out : pointer on output buffer
+* inputIndex : pointer on current input index
+* vl, vr : left and right gain
+* phaseFraction : pointer on current phase fraction
+* phaseIncrement
+* Ouput:
+* outputIndex :
+* out : updated buffer
+* inputIndex : index of next to use
+* phaseFraction : phase fraction for next interpolation
+*
+*******************************************************************/
+void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
+ size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
+ uint32_t &phaseFraction, uint32_t phaseIncrement)
+{
+#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex)
+
+ asm(
+ "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
+ // get parameters
+ " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction
+ " ldr r6, [r6]\n" // phaseFraction
+ " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex
+ " ldr r7, [r7]\n" // inputIndex
+ " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out
+ " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex
+ " ldr r0, [r0]\n" // outputIndex
+ " add r8, r0, asl #2\n" // curOut
+ " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement
+ " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl
+ " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr
+
+ // r0 pin, x0, Samp
+
+ // r1 in
+ // r2 maxOutPt
+ // r3 maxInIdx
+
+ // r4 x1, i1, i3, Out1
+ // r5 out0
+
+ // r6 frac
+ // r7 inputIndex
+ // r8 curOut
+
+ // r9 inc
+ // r10 vl
+ // r11 vr
+
+ // r12
+ // r13 sp
+ // r14
+
+ // the following loop works on 2 frames
+
+ ".Y4L01:\n"
+ " cmp r8, r2\n" // curOut - maxCurOut
+ " bcs .Y4L02\n"
+
+#define MO_ONE_FRAME \
+ " add r0, r1, r7, asl #1\n" /* in + inputIndex */\
+ " ldrsh r4, [r0]\n" /* in[inputIndex] */\
+ " ldr r5, [r8]\n" /* out[outputIndex] */\
+ " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\
+ " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\
+ " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\
+ " mov r4, r4, lsl #2\n" /* <<2 */\
+ " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\
+ " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\
+ " add r0, r0, r4\n" /* x0 - (..) */\
+ " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\
+ " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\
+ " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\
+ " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\
+ " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\
+ " str r4, [r8], #4\n" /* out[outputIndex++] = ... */
+
+ MO_ONE_FRAME // frame 1
+ MO_ONE_FRAME // frame 2
+
+ " cmp r7, r3\n" // inputIndex - maxInIdx
+ " bcc .Y4L01\n"
+ ".Y4L02:\n"
+
+ " bic r6, r6, #0xC0000000\n" // phaseFraction & ...
+ // save modified values
+ " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction
+ " str r6, [r0]\n" // phaseFraction
+ " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex
+ " str r7, [r0]\n" // inputIndex
+ " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out
+ " sub r8, r0\n" // curOut - out
+ " asr r8, #2\n" // new outputIndex
+ " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex
+ " str r8, [r0]\n" // save outputIndex
+
+ " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
+ );
+}
+
+/*******************************************************************
+*
+* AsmStereo16Loop
+* asm optimized stereo loop version; one loop is 2 frames
+* Input:
+* in : pointer on input samples
+* maxOutPt : pointer on first not filled
+* maxInIdx : index on first not used
+* outputIndex : pointer on current output index
+* out : pointer on output buffer
+* inputIndex : pointer on current input index
+* vl, vr : left and right gain
+* phaseFraction : pointer on current phase fraction
+* phaseIncrement
+* Ouput:
+* outputIndex :
+* out : updated buffer
+* inputIndex : index of next to use
+* phaseFraction : phase fraction for next interpolation
+*
+*******************************************************************/
+void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx,
+ size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr,
+ uint32_t &phaseFraction, uint32_t phaseIncrement)
+{
+#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex)
+ asm(
+ "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n"
+ // get parameters
+ " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction
+ " ldr r6, [r6]\n" // phaseFraction
+ " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex
+ " ldr r7, [r7]\n" // inputIndex
+ " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out
+ " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex
+ " ldr r0, [r0]\n" // outputIndex
+ " add r8, r0, asl #2\n" // curOut
+ " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement
+ " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl
+ " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr
+
+ // r0 pin, x0, Samp
+
+ // r1 in
+ // r2 maxOutPt
+ // r3 maxInIdx
+
+ // r4 x1, i1, i3, out1
+ // r5 out0
+
+ // r6 frac
+ // r7 inputIndex
+ // r8 curOut
+
+ // r9 inc
+ // r10 vl
+ // r11 vr
+
+ // r12 temporary
+ // r13 sp
+ // r14
+
+ ".Y5L01:\n"
+ " cmp r8, r2\n" // curOut - maxCurOut
+ " bcs .Y5L02\n"
+
+#define ST_ONE_FRAME \
+ " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\
+\
+ " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\
+\
+ " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\
+ " ldr r5, [r8]\n" /* out[outputIndex] */\
+ " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\
+ " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\
+ " mov r4, r4, lsl #2\n" /* <<2 */\
+ " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\
+ " add r12, r12, r4\n" /* x0 - (..) */\
+ " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\
+ " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\
+ " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\
+\
+ " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\
+ " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\
+ " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\
+ " mov r12, r12, lsl #2\n" /* <<2 */\
+ " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\
+ " add r12, r0, r12\n" /* x0 - (..) */\
+ " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\
+ " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\
+\
+ " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\
+ " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */
+
+ ST_ONE_FRAME // frame 1
+ ST_ONE_FRAME // frame 1
+
+ " cmp r7, r3\n" // inputIndex - maxInIdx
+ " bcc .Y5L01\n"
+ ".Y5L02:\n"
+
+ " bic r6, r6, #0xC0000000\n" // phaseFraction & ...
+ // save modified values
+ " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction
+ " str r6, [r0]\n" // phaseFraction
+ " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex
+ " str r7, [r0]\n" // inputIndex
+ " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out
+ " sub r8, r0\n" // curOut - out
+ " asr r8, #2\n" // new outputIndex
+ " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex
+ " str r8, [r0]\n" // save outputIndex
+
+ " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n"
+ );
+}
+
+#endif // ASM_ARM_RESAMP1
+
+
+// ----------------------------------------------------------------------------
+}
+; // namespace android
+
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
new file mode 100644
index 0000000..2dfac76
--- /dev/null
+++ b/services/audioflinger/AudioResampler.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_RESAMPLER_H
+#define ANDROID_AUDIO_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "AudioBufferProvider.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class AudioResampler {
+public:
+ // Determines quality of SRC.
+ // LOW_QUALITY: linear interpolator (1st order)
+ // MED_QUALITY: cubic interpolator (3rd order)
+ // HIGH_QUALITY: fixed multi-tap FIR (e.g. 48KHz->44.1KHz)
+ // NOTE: high quality SRC will only be supported for
+ // certain fixed rate conversions. Sample rate cannot be
+ // changed dynamically.
+ enum src_quality {
+ DEFAULT=0,
+ LOW_QUALITY=1,
+ MED_QUALITY=2,
+ HIGH_QUALITY=3
+ };
+
+ static AudioResampler* create(int bitDepth, int inChannelCount,
+ int32_t sampleRate, int quality=DEFAULT);
+
+ virtual ~AudioResampler();
+
+ virtual void init() = 0;
+ virtual void setSampleRate(int32_t inSampleRate);
+ virtual void setVolume(int16_t left, int16_t right);
+
+ virtual void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) = 0;
+
+protected:
+ // number of bits for phase fraction - 30 bits allows nearly 2x downsampling
+ static const int kNumPhaseBits = 30;
+
+ // phase mask for fraction
+ static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1;
+
+ // multiplier to calculate fixed point phase increment
+ static const double kPhaseMultiplier = 1L << kNumPhaseBits;
+
+ enum format {MONO_16_BIT, STEREO_16_BIT};
+ AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate);
+
+ // prevent copying
+ AudioResampler(const AudioResampler&);
+ AudioResampler& operator=(const AudioResampler&);
+
+ int32_t mBitDepth;
+ int32_t mChannelCount;
+ int32_t mSampleRate;
+ int32_t mInSampleRate;
+ AudioBufferProvider::Buffer mBuffer;
+ union {
+ int16_t mVolume[2];
+ uint32_t mVolumeRL;
+ };
+ int16_t mTargetVolume[2];
+ format mFormat;
+ size_t mInputIndex;
+ int32_t mPhaseIncrement;
+ uint32_t mPhaseFraction;
+};
+
+// ----------------------------------------------------------------------------
+}
+; // namespace android
+
+#endif // ANDROID_AUDIO_RESAMPLER_H
diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp
new file mode 100644
index 0000000..1d247bd
--- /dev/null
+++ b/services/audioflinger/AudioResamplerCubic.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "AudioResampler.h"
+#include "AudioResamplerCubic.h"
+
+#define LOG_TAG "AudioSRC"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+void AudioResamplerCubic::init() {
+ memset(&left, 0, sizeof(state));
+ memset(&right, 0, sizeof(state));
+}
+
+void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ // should never happen, but we overflow if it does
+ // LOG_ASSERT(outFrameCount < 32767);
+
+ // select the appropriate resampler
+ switch (mChannelCount) {
+ case 1:
+ resampleMono16(out, outFrameCount, provider);
+ break;
+ case 2:
+ resampleStereo16(out, outFrameCount, provider);
+ break;
+ }
+}
+
+void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ int32_t vl = mVolume[0];
+ int32_t vr = mVolume[1];
+
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2;
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
+ // fetch first buffer
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL)
+ return;
+ // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
+ }
+ int16_t *in = mBuffer.i16;
+
+ while (outputIndex < outputSampleCount) {
+ int32_t sample;
+ int32_t x;
+
+ // calculate output sample
+ x = phaseFraction >> kPreInterpShift;
+ out[outputIndex++] += vl * interp(&left, x);
+ out[outputIndex++] += vr * interp(&right, x);
+ // out[outputIndex++] += vr * in[inputIndex*2];
+
+ // increment phase
+ phaseFraction += phaseIncrement;
+ uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits);
+ phaseFraction &= kPhaseMask;
+
+ // time to fetch another sample
+ while (indexIncrement--) {
+
+ inputIndex++;
+ if (inputIndex == mBuffer.frameCount) {
+ inputIndex = 0;
+ provider->releaseBuffer(&mBuffer);
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL)
+ goto save_state; // ugly, but efficient
+ in = mBuffer.i16;
+ // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount);
+ }
+
+ // advance sample state
+ advance(&left, in[inputIndex*2]);
+ advance(&right, in[inputIndex*2+1]);
+ }
+ }
+
+save_state:
+ // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider) {
+
+ int32_t vl = mVolume[0];
+ int32_t vr = mVolume[1];
+
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2;
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
+ // fetch first buffer
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL)
+ return;
+ // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount);
+ }
+ int16_t *in = mBuffer.i16;
+
+ while (outputIndex < outputSampleCount) {
+ int32_t sample;
+ int32_t x;
+
+ // calculate output sample
+ x = phaseFraction >> kPreInterpShift;
+ sample = interp(&left, x);
+ out[outputIndex++] += vl * sample;
+ out[outputIndex++] += vr * sample;
+
+ // increment phase
+ phaseFraction += phaseIncrement;
+ uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits);
+ phaseFraction &= kPhaseMask;
+
+ // time to fetch another sample
+ while (indexIncrement--) {
+
+ inputIndex++;
+ if (inputIndex == mBuffer.frameCount) {
+ inputIndex = 0;
+ provider->releaseBuffer(&mBuffer);
+ mBuffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&mBuffer);
+ if (mBuffer.raw == NULL)
+ goto save_state; // ugly, but efficient
+ // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount);
+ in = mBuffer.i16;
+ }
+
+ // advance sample state
+ advance(&left, in[inputIndex]);
+ }
+ }
+
+save_state:
+ // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction);
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+// ----------------------------------------------------------------------------
+}
+; // namespace android
+
diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h
new file mode 100644
index 0000000..b72b62a
--- /dev/null
+++ b/services/audioflinger/AudioResamplerCubic.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_RESAMPLER_CUBIC_H
+#define ANDROID_AUDIO_RESAMPLER_CUBIC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "AudioResampler.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+class AudioResamplerCubic : public AudioResampler {
+public:
+ AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) :
+ AudioResampler(bitDepth, inChannelCount, sampleRate) {
+ }
+ virtual void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+private:
+ // number of bits used in interpolation multiply - 14 bits avoids overflow
+ static const int kNumInterpBits = 14;
+
+ // bits to shift the phase fraction down to avoid overflow
+ static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits;
+ typedef struct {
+ int32_t a, b, c, y0, y1, y2, y3;
+ } state;
+ void init();
+ void resampleMono16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+ void resampleStereo16(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+ static inline int32_t interp(state* p, int32_t x) {
+ return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1;
+ }
+ static inline void advance(state* p, int16_t in) {
+ p->y0 = p->y1;
+ p->y1 = p->y2;
+ p->y2 = p->y3;
+ p->y3 = in;
+ p->a = (3 * (p->y1 - p->y2) - p->y0 + p->y3) >> 1;
+ p->b = (p->y2 << 1) + p->y0 - (((5 * p->y1 + p->y3)) >> 1);
+ p->c = (p->y2 - p->y0) >> 1;
+ }
+ state left, right;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
new file mode 100644
index 0000000..9e5e254
--- /dev/null
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include "AudioResamplerSinc.h"
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+
+/*
+ * These coeficients are computed with the "fir" utility found in
+ * tools/resampler_tools
+ * TODO: A good optimization would be to transpose this matrix, to take
+ * better advantage of the data-cache.
+ */
+const int32_t AudioResamplerSinc::mFirCoefsUp[] = {
+ 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621,
+ 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9,
+ 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9,
+ 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798,
+ 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636,
+ 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2,
+ 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070,
+ 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000 // this one is needed for lerping the last coefficient
+};
+
+/*
+ * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz)
+ * It's possible to use the above coefficient for any down-sampling
+ * at the expense of a slower processing loop (we can interpolate
+ * these coefficient from the above by "Stretching" them in time).
+ */
+const int32_t AudioResamplerSinc::mFirCoefsDown[] = {
+ 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540,
+ 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4,
+ 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa,
+ 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066,
+ 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf,
+ 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d,
+ 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a,
+ 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000,
+ 0x00000000 // this one is needed for lerping the last coefficient
+};
+
+// ----------------------------------------------------------------------------
+
+static inline
+int32_t mulRL(int left, int32_t in, uint32_t vRL)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ if (left) {
+ asm( "smultb %[out], %[in], %[vRL] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [vRL]"r"(vRL)
+ : );
+ } else {
+ asm( "smultt %[out], %[in], %[vRL] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [vRL]"r"(vRL)
+ : );
+ }
+ return out;
+#else
+ if (left) {
+ return int16_t(in>>16) * int16_t(vRL&0xFFFF);
+ } else {
+ return int16_t(in>>16) * int16_t(vRL>>16);
+ }
+#endif
+}
+
+static inline
+int32_t mulAdd(int16_t in, int32_t v, int32_t a)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ asm( "smlawb %[out], %[v], %[in], %[a] \n"
+ : [out]"=r"(out)
+ : [in]"%r"(in), [v]"r"(v), [a]"r"(a)
+ : );
+ return out;
+#else
+ return a + in * (v>>16);
+ // improved precision
+ // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16);
+#endif
+}
+
+static inline
+int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ int32_t out;
+ if (left) {
+ asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ } else {
+ asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
+ : [out]"=r"(out)
+ : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
+ : );
+ }
+ return out;
+#else
+ if (left) {
+ return a + (int16_t(inRL&0xFFFF) * (v>>16));
+ //improved precision
+ // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16);
+ } else {
+ return a + (int16_t(inRL>>16) * (v>>16));
+ }
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
+ int inChannelCount, int32_t sampleRate)
+ : AudioResampler(bitDepth, inChannelCount, sampleRate),
+ mState(0)
+{
+ /*
+ * Layout of the state buffer for 32 tap:
+ *
+ * "present" sample beginning of 2nd buffer
+ * v v
+ * 0 01 2 23 3
+ * 0 F0 0 F0 F
+ * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn]
+ * ^ ^ head
+ *
+ * p = past samples, convoluted with the (p)ositive side of sinc()
+ * n = future samples, convoluted with the (n)egative side of sinc()
+ * r = extra space for implementing the ring buffer
+ *
+ */
+
+ const size_t numCoefs = 2*halfNumCoefs;
+ const size_t stateSize = numCoefs * inChannelCount * 2;
+ mState = new int16_t[stateSize];
+ memset(mState, 0, sizeof(int16_t)*stateSize);
+ mImpulse = mState + (halfNumCoefs-1)*inChannelCount;
+ mRingFull = mImpulse + (numCoefs+1)*inChannelCount;
+}
+
+AudioResamplerSinc::~AudioResamplerSinc()
+{
+ delete [] mState;
+}
+
+void AudioResamplerSinc::init() {
+}
+
+void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider)
+{
+ mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
+
+ // select the appropriate resampler
+ switch (mChannelCount) {
+ case 1:
+ resample<1>(out, outFrameCount, provider);
+ break;
+ case 2:
+ resample<2>(out, outFrameCount, provider);
+ break;
+ }
+}
+
+
+template<int CHANNELS>
+void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider)
+{
+ int16_t* impulse = mImpulse;
+ uint32_t vRL = mVolumeRL;
+ size_t inputIndex = mInputIndex;
+ uint32_t phaseFraction = mPhaseFraction;
+ uint32_t phaseIncrement = mPhaseIncrement;
+ size_t outputIndex = 0;
+ size_t outputSampleCount = outFrameCount * 2;
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
+ AudioBufferProvider::Buffer& buffer(mBuffer);
+ while (outputIndex < outputSampleCount) {
+ // buffer is empty, fetch a new one
+ while (buffer.frameCount == 0) {
+ buffer.frameCount = inFrameCount;
+ provider->getNextBuffer(&buffer);
+ if (buffer.raw == NULL) {
+ goto resample_exit;
+ }
+ const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ if (phaseIndex == 1) {
+ // read one frame
+ read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+ } else if (phaseIndex == 2) {
+ // read 2 frames
+ read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+ inputIndex++;
+ if (inputIndex >= mBuffer.frameCount) {
+ inputIndex -= mBuffer.frameCount;
+ provider->releaseBuffer(&buffer);
+ } else {
+ read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+ }
+ }
+ }
+ int16_t *in = buffer.i16;
+ const size_t frameCount = buffer.frameCount;
+
+ // Always read-in the first samples from the input buffer
+ int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ head[0] = in[inputIndex*CHANNELS + 0];
+ if (CHANNELS == 2)
+ head[1] = in[inputIndex*CHANNELS + 1];
+
+ // handle boundary case
+ int32_t l, r;
+ while (outputIndex < outputSampleCount) {
+ filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse);
+ out[outputIndex++] += 2 * mulRL(1, l, vRL);
+ out[outputIndex++] += 2 * mulRL(0, r, vRL);
+
+ phaseFraction += phaseIncrement;
+ const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ if (phaseIndex == 1) {
+ inputIndex++;
+ if (inputIndex >= frameCount)
+ break; // need a new buffer
+ read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
+ } else if(phaseIndex == 2) { // maximum value
+ inputIndex++;
+ if (inputIndex >= frameCount)
+ break; // 0 frame available, 2 frames needed
+ // read first frame
+ read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
+ inputIndex++;
+ if (inputIndex >= frameCount)
+ break; // 0 frame available, 1 frame needed
+ // read second frame
+ read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
+ }
+ }
+
+ // if done with buffer, save samples
+ if (inputIndex >= frameCount) {
+ inputIndex -= frameCount;
+ provider->releaseBuffer(&buffer);
+ }
+ }
+
+resample_exit:
+ mImpulse = impulse;
+ mInputIndex = inputIndex;
+ mPhaseFraction = phaseFraction;
+}
+
+template<int CHANNELS>
+/***
+* read()
+*
+* This function reads only one frame from input buffer and writes it in
+* state buffer
+*
+**/
+void AudioResamplerSinc::read(
+ int16_t*& impulse, uint32_t& phaseFraction,
+ int16_t const* in, size_t inputIndex)
+{
+ const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ impulse += CHANNELS;
+ phaseFraction -= 1LU<<kNumPhaseBits;
+ if (impulse >= mRingFull) {
+ const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
+ memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
+ impulse -= stateSize;
+ }
+ int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ head[0] = in[inputIndex*CHANNELS + 0];
+ if (CHANNELS == 2)
+ head[1] = in[inputIndex*CHANNELS + 1];
+}
+
+template<int CHANNELS>
+void AudioResamplerSinc::filterCoefficient(
+ int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples)
+{
+ // compute the index of the coefficient on the positive side and
+ // negative side
+ uint32_t indexP = (phase & cMask) >> cShift;
+ uint16_t lerpP = (phase & pMask) >> pShift;
+ uint32_t indexN = (-phase & cMask) >> cShift;
+ uint16_t lerpN = (-phase & pMask) >> pShift;
+ if ((indexP == 0) && (lerpP == 0)) {
+ indexN = cMask >> cShift;
+ lerpN = pMask >> pShift;
+ }
+
+ l = 0;
+ r = 0;
+ int32_t const* coefs = mFirCoefs;
+ int16_t const *sP = samples;
+ int16_t const *sN = samples+CHANNELS;
+ for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) {
+ interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
+ interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
+ interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
+ interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
+ interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
+ sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ }
+}
+
+template<int CHANNELS>
+void AudioResamplerSinc::interpolate(
+ int32_t& l, int32_t& r,
+ int32_t const* coefs, int16_t lerp, int16_t const* samples)
+{
+ int32_t c0 = coefs[0];
+ int32_t c1 = coefs[1];
+ int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
+ if (CHANNELS == 2) {
+ uint32_t rl = *reinterpret_cast<uint32_t const*>(samples);
+ l = mulAddRL(1, rl, sinc, l);
+ r = mulAddRL(0, rl, sinc, r);
+ } else {
+ r = l = mulAdd(samples[0], sinc, l);
+ }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
new file mode 100644
index 0000000..e6cb90b
--- /dev/null
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_RESAMPLER_SINC_H
+#define ANDROID_AUDIO_RESAMPLER_SINC_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+
+#include "AudioResampler.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class AudioResamplerSinc : public AudioResampler {
+public:
+ AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate);
+
+ ~AudioResamplerSinc();
+
+ virtual void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+private:
+ void init();
+
+ template<int CHANNELS>
+ void resample(int32_t* out, size_t outFrameCount,
+ AudioBufferProvider* provider);
+
+ template<int CHANNELS>
+ inline void filterCoefficient(
+ int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples);
+
+ template<int CHANNELS>
+ inline void interpolate(
+ int32_t& l, int32_t& r,
+ int32_t const* coefs, int16_t lerp, int16_t const* samples);
+
+ template<int CHANNELS>
+ inline void read(int16_t*& impulse, uint32_t& phaseFraction,
+ int16_t const* in, size_t inputIndex);
+
+ int16_t *mState;
+ int16_t *mImpulse;
+ int16_t *mRingFull;
+
+ int32_t const * mFirCoefs;
+ static const int32_t mFirCoefsDown[];
+ static const int32_t mFirCoefsUp[];
+
+ // ----------------------------------------------------------------------------
+ static const int32_t RESAMPLE_FIR_NUM_COEF = 8;
+ static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4;
+
+ // we have 16 coefs samples per zero-crossing
+ static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4
+ static const int cShift = kNumPhaseBits - coefsBits; // 26
+ static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000
+
+ // and we use 15 bits to interpolate between these samples
+ // this cannot change because the mul below rely on it.
+ static const int pLerpBits = 15;
+ static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11
+ static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11
+
+ // number of zero-crossing on each side
+ static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
new file mode 100644
index 0000000..df5c166
--- /dev/null
+++ b/services/camera/libcameraservice/Android.mk
@@ -0,0 +1,71 @@
+LOCAL_PATH:= $(call my-dir)
+
+#
+# Set USE_CAMERA_STUB for non-emulator and non-simulator builds, if you want
+# the camera service to use the fake camera. For emulator or simulator builds,
+# we always use the fake camera.
+
+ifeq ($(USE_CAMERA_STUB),)
+USE_CAMERA_STUB:=false
+ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),)
+USE_CAMERA_STUB:=true
+endif #libcamerastub
+endif
+
+ifeq ($(USE_CAMERA_STUB),true)
+#
+# libcamerastub
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ CameraHardwareStub.cpp \
+ FakeCamera.cpp
+
+LOCAL_MODULE:= libcamerastub
+
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_CFLAGS += -DSINGLE_PROCESS
+endif
+
+LOCAL_SHARED_LIBRARIES:= libui
+
+include $(BUILD_STATIC_LIBRARY)
+endif # USE_CAMERA_STUB
+
+#
+# libcameraservice
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ CameraService.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+ libui \
+ libutils \
+ libbinder \
+ libcutils \
+ libmedia \
+ libcamera_client \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= libcameraservice
+
+LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"
+
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_CFLAGS += -DSINGLE_PROCESS
+endif
+
+ifeq ($(USE_CAMERA_STUB), true)
+LOCAL_STATIC_LIBRARIES += libcamerastub
+LOCAL_CFLAGS += -include CameraHardwareStub.h
+else
+LOCAL_SHARED_LIBRARIES += libcamera
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
new file mode 100644
index 0000000..8b66389
--- /dev/null
+++ b/services/camera/libcameraservice/CameraHardwareStub.cpp
@@ -0,0 +1,402 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "CameraHardwareStub"
+#include <utils/Log.h>
+
+#include "CameraHardwareStub.h"
+#include <utils/threads.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "CannedJpeg.h"
+
+namespace android {
+
+CameraHardwareStub::CameraHardwareStub()
+ : mParameters(),
+ mPreviewHeap(0),
+ mRawHeap(0),
+ mFakeCamera(0),
+ mPreviewFrameSize(0),
+ mNotifyCb(0),
+ mDataCb(0),
+ mDataCbTimestamp(0),
+ mCallbackCookie(0),
+ mMsgEnabled(0),
+ mCurrentPreviewFrame(0)
+{
+ initDefaultParameters();
+}
+
+void CameraHardwareStub::initDefaultParameters()
+{
+ CameraParameters p;
+
+ p.set("preview-size-values","320x240");
+ p.setPreviewSize(320, 240);
+ p.setPreviewFrameRate(15);
+ p.setPreviewFormat("yuv422sp");
+
+ p.set("picture-size-values", "320x240");
+ p.setPictureSize(320, 240);
+ p.setPictureFormat("jpeg");
+
+ if (setParameters(p) != NO_ERROR) {
+ LOGE("Failed to set default parameters?!");
+ }
+}
+
+void CameraHardwareStub::initHeapLocked()
+{
+ // Create raw heap.
+ int picture_width, picture_height;
+ mParameters.getPictureSize(&picture_width, &picture_height);
+ mRawHeap = new MemoryHeapBase(picture_width * 2 * picture_height);
+
+ int preview_width, preview_height;
+ mParameters.getPreviewSize(&preview_width, &preview_height);
+ LOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height);
+
+ // Note that we enforce yuv422 in setParameters().
+ int how_big = preview_width * preview_height * 2;
+
+ // If we are being reinitialized to the same size as before, no
+ // work needs to be done.
+ if (how_big == mPreviewFrameSize)
+ return;
+
+ mPreviewFrameSize = how_big;
+
+ // Make a new mmap'ed heap that can be shared across processes.
+ // use code below to test with pmem
+ mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount);
+ // Make an IMemory for each frame so that we can reuse them in callbacks.
+ for (int i = 0; i < kBufferCount; i++) {
+ mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize);
+ }
+
+ // Recreate the fake camera to reflect the current size.
+ delete mFakeCamera;
+ mFakeCamera = new FakeCamera(preview_width, preview_height);
+}
+
+CameraHardwareStub::~CameraHardwareStub()
+{
+ delete mFakeCamera;
+ mFakeCamera = 0; // paranoia
+ singleton.clear();
+}
+
+sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
+{
+ return mPreviewHeap;
+}
+
+sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
+{
+ return mRawHeap;
+}
+
+void CameraHardwareStub::setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user)
+{
+ Mutex::Autolock lock(mLock);
+ mNotifyCb = notify_cb;
+ mDataCb = data_cb;
+ mDataCbTimestamp = data_cb_timestamp;
+ mCallbackCookie = user;
+}
+
+void CameraHardwareStub::enableMsgType(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ mMsgEnabled |= msgType;
+}
+
+void CameraHardwareStub::disableMsgType(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ mMsgEnabled &= ~msgType;
+}
+
+bool CameraHardwareStub::msgTypeEnabled(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ return (mMsgEnabled & msgType);
+}
+
+// ---------------------------------------------------------------------------
+
+int CameraHardwareStub::previewThread()
+{
+ mLock.lock();
+ // the attributes below can change under our feet...
+
+ int previewFrameRate = mParameters.getPreviewFrameRate();
+
+ // Find the offset within the heap of the current buffer.
+ ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize;
+
+ sp<MemoryHeapBase> heap = mPreviewHeap;
+
+ // this assumes the internal state of fake camera doesn't change
+ // (or is thread safe)
+ FakeCamera* fakeCamera = mFakeCamera;
+
+ sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame];
+
+ mLock.unlock();
+
+ // TODO: here check all the conditions that could go wrong
+ if (buffer != 0) {
+ // Calculate how long to wait between frames.
+ int delay = (int)(1000000.0f / float(previewFrameRate));
+
+ // This is always valid, even if the client died -- the memory
+ // is still mapped in our process.
+ void *base = heap->base();
+
+ // Fill the current frame with the fake camera.
+ uint8_t *frame = ((uint8_t *)base) + offset;
+ fakeCamera->getNextFrameAsYuv422(frame);
+
+ //LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
+
+ // Notify the client of a new frame.
+ if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
+ mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);
+
+ // Advance the buffer pointer.
+ mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
+
+ // Wait for it...
+ usleep(delay);
+ }
+
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::startPreview()
+{
+ Mutex::Autolock lock(mLock);
+ if (mPreviewThread != 0) {
+ // already running
+ return INVALID_OPERATION;
+ }
+ mPreviewThread = new PreviewThread(this);
+ return NO_ERROR;
+}
+
+void CameraHardwareStub::stopPreview()
+{
+ sp<PreviewThread> previewThread;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mLock);
+ previewThread = mPreviewThread;
+ }
+
+ // don't hold the lock while waiting for the thread to quit
+ if (previewThread != 0) {
+ previewThread->requestExitAndWait();
+ }
+
+ Mutex::Autolock lock(mLock);
+ mPreviewThread.clear();
+}
+
+bool CameraHardwareStub::previewEnabled() {
+ return mPreviewThread != 0;
+}
+
+status_t CameraHardwareStub::startRecording()
+{
+ return UNKNOWN_ERROR;
+}
+
+void CameraHardwareStub::stopRecording()
+{
+}
+
+bool CameraHardwareStub::recordingEnabled()
+{
+ return false;
+}
+
+void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem)
+{
+}
+
+// ---------------------------------------------------------------------------
+
+int CameraHardwareStub::beginAutoFocusThread(void *cookie)
+{
+ CameraHardwareStub *c = (CameraHardwareStub *)cookie;
+ return c->autoFocusThread();
+}
+
+int CameraHardwareStub::autoFocusThread()
+{
+ if (mMsgEnabled & CAMERA_MSG_FOCUS)
+ mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie);
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::autoFocus()
+{
+ Mutex::Autolock lock(mLock);
+ if (createThread(beginAutoFocusThread, this) == false)
+ return UNKNOWN_ERROR;
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::cancelAutoFocus()
+{
+ return NO_ERROR;
+}
+
+/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie)
+{
+ CameraHardwareStub *c = (CameraHardwareStub *)cookie;
+ return c->pictureThread();
+}
+
+int CameraHardwareStub::pictureThread()
+{
+ if (mMsgEnabled & CAMERA_MSG_SHUTTER)
+ mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);
+
+ if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
+ //FIXME: use a canned YUV image!
+ // In the meantime just make another fake camera picture.
+ int w, h;
+ mParameters.getPictureSize(&w, &h);
+ sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
+ FakeCamera cam(w, h);
+ cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
+ mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
+ }
+
+ if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
+ sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
+ sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
+ memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
+ mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);
+ }
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::takePicture()
+{
+ stopPreview();
+ if (createThread(beginPictureThread, this) == false)
+ return -1;
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::cancelPicture()
+{
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ AutoMutex lock(&mLock);
+ if (mFakeCamera != 0) {
+ mFakeCamera->dump(fd);
+ mParameters.dump(fd, args);
+ snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false");
+ result.append(buffer);
+ } else {
+ result.append("No camera client yet.\n");
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t CameraHardwareStub::setParameters(const CameraParameters& params)
+{
+ Mutex::Autolock lock(mLock);
+ // XXX verify params
+
+ if (strcmp(params.getPreviewFormat(), "yuv422sp") != 0) {
+ LOGE("Only yuv422sp preview is supported");
+ return -1;
+ }
+
+ if (strcmp(params.getPictureFormat(), "jpeg") != 0) {
+ LOGE("Only jpeg still pictures are supported");
+ return -1;
+ }
+
+ int w, h;
+ params.getPictureSize(&w, &h);
+ if (w != kCannedJpegWidth && h != kCannedJpegHeight) {
+ LOGE("Still picture size must be size of canned JPEG (%dx%d)",
+ kCannedJpegWidth, kCannedJpegHeight);
+ return -1;
+ }
+
+ mParameters = params;
+ initHeapLocked();
+
+ return NO_ERROR;
+}
+
+CameraParameters CameraHardwareStub::getParameters() const
+{
+ Mutex::Autolock lock(mLock);
+ return mParameters;
+}
+
+status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1,
+ int32_t arg2)
+{
+ return BAD_VALUE;
+}
+
+void CameraHardwareStub::release()
+{
+}
+
+wp<CameraHardwareInterface> CameraHardwareStub::singleton;
+
+sp<CameraHardwareInterface> CameraHardwareStub::createInstance()
+{
+ if (singleton != 0) {
+ sp<CameraHardwareInterface> hardware = singleton.promote();
+ if (hardware != 0) {
+ return hardware;
+ }
+ }
+ sp<CameraHardwareInterface> hardware(new CameraHardwareStub());
+ singleton = hardware;
+ return hardware;
+}
+
+extern "C" sp<CameraHardwareInterface> openCameraHardware()
+{
+ return CameraHardwareStub::createInstance();
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
new file mode 100644
index 0000000..957813a4
--- /dev/null
+++ b/services/camera/libcameraservice/CameraHardwareStub.h
@@ -0,0 +1,135 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
+#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H
+
+#include "FakeCamera.h"
+#include <utils/threads.h>
+#include <camera/CameraHardwareInterface.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CameraHardwareStub : public CameraHardwareInterface {
+public:
+ virtual sp<IMemoryHeap> getPreviewHeap() const;
+ virtual sp<IMemoryHeap> getRawHeap() const;
+
+ virtual void setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user);
+
+ virtual void enableMsgType(int32_t msgType);
+ virtual void disableMsgType(int32_t msgType);
+ virtual bool msgTypeEnabled(int32_t msgType);
+
+ virtual status_t startPreview();
+ virtual void stopPreview();
+ virtual bool previewEnabled();
+
+ virtual status_t startRecording();
+ virtual void stopRecording();
+ virtual bool recordingEnabled();
+ virtual void releaseRecordingFrame(const sp<IMemory>& mem);
+
+ virtual status_t autoFocus();
+ virtual status_t cancelAutoFocus();
+ virtual status_t takePicture();
+ virtual status_t cancelPicture();
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual status_t setParameters(const CameraParameters& params);
+ virtual CameraParameters getParameters() const;
+ virtual status_t sendCommand(int32_t command, int32_t arg1,
+ int32_t arg2);
+ virtual void release();
+
+ static sp<CameraHardwareInterface> createInstance();
+
+private:
+ CameraHardwareStub();
+ virtual ~CameraHardwareStub();
+
+ static wp<CameraHardwareInterface> singleton;
+
+ static const int kBufferCount = 4;
+
+ class PreviewThread : public Thread {
+ CameraHardwareStub* mHardware;
+ public:
+ PreviewThread(CameraHardwareStub* hw) :
+#ifdef SINGLE_PROCESS
+ // In single process mode this thread needs to be a java thread,
+ // since we won't be calling through the binder.
+ Thread(true),
+#else
+ Thread(false),
+#endif
+ mHardware(hw) { }
+ virtual void onFirstRef() {
+ run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY);
+ }
+ virtual bool threadLoop() {
+ mHardware->previewThread();
+ // loop until we need to quit
+ return true;
+ }
+ };
+
+ void initDefaultParameters();
+ void initHeapLocked();
+
+ int previewThread();
+
+ static int beginAutoFocusThread(void *cookie);
+ int autoFocusThread();
+
+ static int beginPictureThread(void *cookie);
+ int pictureThread();
+
+ mutable Mutex mLock;
+
+ CameraParameters mParameters;
+
+ sp<MemoryHeapBase> mPreviewHeap;
+ sp<MemoryHeapBase> mRawHeap;
+ sp<MemoryBase> mBuffers[kBufferCount];
+
+ FakeCamera *mFakeCamera;
+ bool mPreviewRunning;
+ int mPreviewFrameSize;
+
+ // protected by mLock
+ sp<PreviewThread> mPreviewThread;
+
+ notify_callback mNotifyCb;
+ data_callback mDataCb;
+ data_callback_timestamp mDataCbTimestamp;
+ void *mCallbackCookie;
+
+ int32_t mMsgEnabled;
+
+ // only used from PreviewThread
+ int mCurrentPreviewFrame;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
new file mode 100644
index 0000000..00bd54e
--- /dev/null
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -0,0 +1,1417 @@
+/*
+**
+** Copyright (C) 2008, The Android Open Source Project
+** Copyright (C) 2008 HTC Inc.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "CameraService"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <utils/String16.h>
+#include <utils/Errors.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <camera/ICameraService.h>
+#include <surfaceflinger/ISurface.h>
+#include <ui/Overlay.h>
+
+#include <hardware/hardware.h>
+
+#include <media/mediaplayer.h>
+#include <media/AudioSystem.h>
+#include "CameraService.h"
+
+#include <cutils/atomic.h>
+
+namespace android {
+
+extern "C" {
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+}
+
+// When you enable this, as well as DEBUG_REFS=1 and
+// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all
+// references to the CameraService::Client in order to catch the case where the
+// client is being destroyed while a callback from the CameraHardwareInterface
+// is outstanding. This is a serious bug because if we make another call into
+// CameraHardwreInterface that itself triggers a callback, we will deadlock.
+
+#define DEBUG_CLIENT_REFERENCES 0
+
+#define PICTURE_TIMEOUT seconds(5)
+
+#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */
+#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0
+#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0
+#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0
+
+#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
+static int debug_frame_cnt;
+#endif
+
+static int getCallingPid() {
+ return IPCThreadState::self()->getCallingPid();
+}
+
+// ----------------------------------------------------------------------------
+
+void CameraService::instantiate() {
+ defaultServiceManager()->addService(
+ String16("media.camera"), new CameraService());
+}
+
+// ----------------------------------------------------------------------------
+
+CameraService::CameraService() :
+ BnCameraService()
+{
+ LOGI("CameraService started: pid=%d", getpid());
+ mUsers = 0;
+}
+
+CameraService::~CameraService()
+{
+ if (mClient != 0) {
+ LOGE("mClient was still connected in destructor!");
+ }
+}
+
+sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
+{
+ int callingPid = getCallingPid();
+ LOGV("CameraService::connect E (pid %d, client %p)", callingPid,
+ cameraClient->asBinder().get());
+
+ Mutex::Autolock lock(mServiceLock);
+ sp<Client> client;
+ if (mClient != 0) {
+ sp<Client> currentClient = mClient.promote();
+ if (currentClient != 0) {
+ sp<ICameraClient> currentCameraClient(currentClient->getCameraClient());
+ if (cameraClient->asBinder() == currentCameraClient->asBinder()) {
+ // This is the same client reconnecting...
+ LOGV("CameraService::connect X (pid %d, same client %p) is reconnecting...",
+ callingPid, cameraClient->asBinder().get());
+ return currentClient;
+ } else {
+ // It's another client... reject it
+ LOGV("CameraService::connect X (pid %d, new client %p) rejected. "
+ "(old pid %d, old client %p)",
+ callingPid, cameraClient->asBinder().get(),
+ currentClient->mClientPid, currentCameraClient->asBinder().get());
+ if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) {
+ LOGV("The old client is dead!");
+ }
+ return client;
+ }
+ } else {
+ // can't promote, the previous client has died...
+ LOGV("New client (pid %d) connecting, old reference was dangling...",
+ callingPid);
+ mClient.clear();
+ }
+ }
+
+ if (mUsers > 0) {
+ LOGV("Still have client, rejected");
+ return client;
+ }
+
+ // create a new Client object
+ client = new Client(this, cameraClient, callingPid);
+ mClient = client;
+#if DEBUG_CLIENT_REFERENCES
+ // Enable tracking for this object, and track increments and decrements of
+ // the refcount.
+ client->trackMe(true, true);
+#endif
+ LOGV("CameraService::connect X");
+ return client;
+}
+
+void CameraService::removeClient(const sp<ICameraClient>& cameraClient)
+{
+ int callingPid = getCallingPid();
+
+ // Declare this outside the lock to make absolutely sure the
+ // destructor won't be called with the lock held.
+ sp<Client> client;
+
+ Mutex::Autolock lock(mServiceLock);
+
+ if (mClient == 0) {
+ // This happens when we have already disconnected.
+ LOGV("removeClient (pid %d): already disconnected", callingPid);
+ return;
+ }
+
+ // Promote mClient. It can fail if we are called from this path:
+ // Client::~Client() -> disconnect() -> removeClient().
+ client = mClient.promote();
+ if (client == 0) {
+ LOGV("removeClient (pid %d): no more strong reference", callingPid);
+ mClient.clear();
+ return;
+ }
+
+ if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) {
+ // ugh! that's not our client!!
+ LOGW("removeClient (pid %d): mClient doesn't match!", callingPid);
+ } else {
+ // okay, good, forget about mClient
+ mClient.clear();
+ }
+
+ LOGV("removeClient (pid %d) done", callingPid);
+}
+
+// The reason we need this count is a new CameraService::connect() request may
+// come in while the previous Client's destructor has not been run or is still
+// running. If the last strong reference of the previous Client is gone but
+// destructor has not been run, we should not allow the new Client to be created
+// because we need to wait for the previous Client to tear down the hardware
+// first.
+void CameraService::incUsers() {
+ android_atomic_inc(&mUsers);
+}
+
+void CameraService::decUsers() {
+ android_atomic_dec(&mUsers);
+}
+
+static sp<MediaPlayer> newMediaPlayer(const char *file)
+{
+ sp<MediaPlayer> mp = new MediaPlayer();
+ if (mp->setDataSource(file, NULL /* headers */) == NO_ERROR) {
+ mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
+ mp->prepare();
+ } else {
+ mp.clear();
+ LOGE("Failed to load CameraService sounds.");
+ }
+ return mp;
+}
+
+CameraService::Client::Client(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient, pid_t clientPid)
+{
+ int callingPid = getCallingPid();
+ LOGV("Client::Client E (pid %d)", callingPid);
+ mCameraService = cameraService;
+ mCameraClient = cameraClient;
+ mClientPid = clientPid;
+ mHardware = openCameraHardware();
+ mUseOverlay = mHardware->useOverlay();
+
+ mHardware->setCallbacks(notifyCallback,
+ dataCallback,
+ dataCallbackTimestamp,
+ mCameraService.get());
+
+ // Enable zoom, error, and focus messages by default
+ mHardware->enableMsgType(CAMERA_MSG_ERROR |
+ CAMERA_MSG_ZOOM |
+ CAMERA_MSG_FOCUS);
+
+ mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
+ mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+ mOverlayW = 0;
+ mOverlayH = 0;
+
+ // Callback is disabled by default
+ mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mOrientation = 0;
+ cameraService->incUsers();
+ LOGV("Client::Client X (pid %d)", callingPid);
+}
+
+status_t CameraService::Client::checkPid()
+{
+ int callingPid = getCallingPid();
+ if (mClientPid == callingPid) return NO_ERROR;
+ LOGW("Attempt to use locked camera (client %p) from different process "
+ " (old pid %d, new pid %d)",
+ getCameraClient()->asBinder().get(), mClientPid, callingPid);
+ return -EBUSY;
+}
+
+status_t CameraService::Client::lock()
+{
+ int callingPid = getCallingPid();
+ LOGV("lock from pid %d (mClientPid %d)", callingPid, mClientPid);
+ Mutex::Autolock _l(mLock);
+ // lock camera to this client if the the camera is unlocked
+ if (mClientPid == 0) {
+ mClientPid = callingPid;
+ return NO_ERROR;
+ }
+ // returns NO_ERROR if the client already owns the camera, -EBUSY otherwise
+ return checkPid();
+}
+
+status_t CameraService::Client::unlock()
+{
+ int callingPid = getCallingPid();
+ LOGV("unlock from pid %d (mClientPid %d)", callingPid, mClientPid);
+ Mutex::Autolock _l(mLock);
+ // allow anyone to use camera
+ status_t result = checkPid();
+ if (result == NO_ERROR) {
+ mClientPid = 0;
+ LOGV("clear mCameraClient (pid %d)", callingPid);
+ // we need to remove the reference so that when app goes
+ // away, the reference count goes to 0.
+ mCameraClient.clear();
+ }
+ return result;
+}
+
+status_t CameraService::Client::connect(const sp<ICameraClient>& client)
+{
+ int callingPid = getCallingPid();
+
+ // connect a new process to the camera
+ LOGV("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get());
+
+ // I hate this hack, but things get really ugly when the media recorder
+ // service is handing back the camera to the app. The ICameraClient
+ // destructor will be called during the same IPC, making it look like
+ // the remote client is trying to disconnect. This hack temporarily
+ // sets the mClientPid to an invalid pid to prevent the hardware from
+ // being torn down.
+ {
+
+ // hold a reference to the old client or we will deadlock if the client is
+ // in the same process and we hold the lock when we remove the reference
+ sp<ICameraClient> oldClient;
+ {
+ Mutex::Autolock _l(mLock);
+ if (mClientPid != 0 && checkPid() != NO_ERROR) {
+ LOGW("Tried to connect to locked camera (old pid %d, new pid %d)",
+ mClientPid, callingPid);
+ return -EBUSY;
+ }
+ oldClient = mCameraClient;
+
+ // did the client actually change?
+ if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) {
+ LOGV("Connect to the same client");
+ return NO_ERROR;
+ }
+
+ mCameraClient = client;
+ mClientPid = -1;
+ mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ LOGV("Connect to the new client (pid %d, client %p)",
+ callingPid, mCameraClient->asBinder().get());
+ }
+
+ }
+ // the old client destructor is called when oldClient goes out of scope
+ // now we set the new PID to lock the interface again
+ mClientPid = callingPid;
+
+ return NO_ERROR;
+}
+
+#if HAVE_ANDROID_OS
+static void *unregister_surface(void *arg)
+{
+ ISurface *surface = (ISurface *)arg;
+ surface->unregisterBuffers();
+ IPCThreadState::self()->flushCommands();
+ return NULL;
+}
+#endif
+
+CameraService::Client::~Client()
+{
+ int callingPid = getCallingPid();
+
+ // tear down client
+ LOGV("Client::~Client E (pid %d, client %p)",
+ callingPid, getCameraClient()->asBinder().get());
+ if (mSurface != 0 && !mUseOverlay) {
+#if HAVE_ANDROID_OS
+ pthread_t thr;
+ // We unregister the buffers in a different thread because binder does
+ // not let us make sychronous transactions in a binder destructor (that
+ // is, upon our reaching a refcount of zero.)
+ pthread_create(&thr, NULL,
+ unregister_surface,
+ mSurface.get());
+ pthread_join(thr, NULL);
+#else
+ mSurface->unregisterBuffers();
+#endif
+ }
+
+ if (mMediaPlayerBeep.get() != NULL) {
+ mMediaPlayerBeep->disconnect();
+ mMediaPlayerBeep.clear();
+ }
+ if (mMediaPlayerClick.get() != NULL) {
+ mMediaPlayerClick->disconnect();
+ mMediaPlayerClick.clear();
+ }
+
+ // make sure we tear down the hardware
+ mClientPid = callingPid;
+ disconnect();
+ LOGV("Client::~Client X (pid %d)", mClientPid);
+}
+
+void CameraService::Client::disconnect()
+{
+ int callingPid = getCallingPid();
+
+ LOGV("Client::disconnect() E (pid %d client %p)",
+ callingPid, getCameraClient()->asBinder().get());
+
+ Mutex::Autolock lock(mLock);
+ if (mClientPid <= 0) {
+ LOGV("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
+ return;
+ }
+ if (checkPid() != NO_ERROR) {
+ LOGV("Different client - don't disconnect");
+ return;
+ }
+
+ // Make sure disconnect() is done once and once only, whether it is called
+ // from the user directly, or called by the destructor.
+ if (mHardware == 0) return;
+
+ LOGV("hardware teardown");
+ // Before destroying mHardware, we must make sure it's in the
+ // idle state.
+ mHardware->stopPreview();
+ // Cancel all picture callbacks.
+ mHardware->disableMsgType(CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+ mHardware->cancelPicture();
+ // Turn off remaining messages.
+ mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
+ // Release the hardware resources.
+ mHardware->release();
+ // Release the held overlay resources.
+ if (mUseOverlay)
+ {
+ mOverlayRef = 0;
+ }
+ mHardware.clear();
+
+ mCameraService->removeClient(mCameraClient);
+ mCameraService->decUsers();
+
+ LOGV("Client::disconnect() X (pid %d)", callingPid);
+}
+
+// pass the buffered ISurface to the camera service
+status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
+{
+ LOGV("setPreviewDisplay(%p) (pid %d)",
+ ((surface == NULL) ? NULL : surface.get()), getCallingPid());
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ Mutex::Autolock surfaceLock(mSurfaceLock);
+ result = NO_ERROR;
+ // asBinder() is safe on NULL (returns NULL)
+ if (surface->asBinder() != mSurface->asBinder()) {
+ if (mSurface != 0) {
+ LOGV("clearing old preview surface %p", mSurface.get());
+ if ( !mUseOverlay)
+ {
+ mSurface->unregisterBuffers();
+ }
+ else
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ }
+ }
+ mSurface = surface;
+ mOverlayRef = 0;
+ // If preview has been already started, set overlay or register preview
+ // buffers now.
+ if (mHardware->previewEnabled()) {
+ if (mUseOverlay) {
+ result = setOverlay();
+ } else if (mSurface != 0) {
+ result = registerPreviewBuffers();
+ }
+ }
+ }
+ return result;
+}
+
+// set the preview callback flag to affect how the received frames from
+// preview are handled.
+void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
+{
+ LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
+ mPreviewCallbackFlag = callback_flag;
+
+ if(mUseOverlay) {
+ if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
+ mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ else
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
+}
+
+// start preview mode
+status_t CameraService::Client::startCameraMode(camera_mode mode)
+{
+ int callingPid = getCallingPid();
+
+ LOGV("startCameraMode(%d) (pid %d)", mode, callingPid);
+
+ /* we cannot call into mHardware with mLock held because
+ * mHardware has callbacks onto us which acquire this lock
+ */
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ switch(mode) {
+ case CAMERA_RECORDING_MODE:
+ if (mSurface == 0) {
+ LOGE("setPreviewDisplay must be called before startRecordingMode.");
+ return INVALID_OPERATION;
+ }
+ return startRecordingMode();
+
+ default: // CAMERA_PREVIEW_MODE
+ if (mSurface == 0) {
+ LOGV("mSurface is not set yet.");
+ }
+ return startPreviewMode();
+ }
+}
+
+status_t CameraService::Client::startRecordingMode()
+{
+ LOGV("startRecordingMode (pid %d)", getCallingPid());
+
+ status_t ret = UNKNOWN_ERROR;
+
+ // if preview has not been started, start preview first
+ if (!mHardware->previewEnabled()) {
+ ret = startPreviewMode();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ }
+
+ // if recording has been enabled, nothing needs to be done
+ if (mHardware->recordingEnabled()) {
+ return NO_ERROR;
+ }
+
+ // start recording mode
+ ret = mHardware->startRecording();
+ if (ret != NO_ERROR) {
+ LOGE("mHardware->startRecording() failed with status %d", ret);
+ }
+ return ret;
+}
+
+status_t CameraService::Client::setOverlay()
+{
+ LOGV("setOverlay");
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ if ( w != mOverlayW || h != mOverlayH )
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ mOverlayRef = 0;
+ }
+
+ status_t ret = NO_ERROR;
+ if (mSurface != 0) {
+ if (mOverlayRef.get() == NULL) {
+
+ // FIXME:
+ // Surfaceflinger may hold onto the previous overlay reference for some
+ // time after we try to destroy it. retry a few times. In the future, we
+ // should make the destroy call block, or possibly specify that we can
+ // wait in the createOverlay call if the previous overlay is in the
+ // process of being destroyed.
+ for (int retry = 0; retry < 50; ++retry) {
+ mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT,
+ mOrientation);
+ if (mOverlayRef != NULL) break;
+ LOGW("Overlay create failed - retrying");
+ usleep(20000);
+ }
+ if ( mOverlayRef.get() == NULL )
+ {
+ LOGE("Overlay Creation Failed!");
+ return -EINVAL;
+ }
+ ret = mHardware->setOverlay(new Overlay(mOverlayRef));
+ }
+ } else {
+ ret = mHardware->setOverlay(NULL);
+ }
+ if (ret != NO_ERROR) {
+ LOGE("mHardware->setOverlay() failed with status %d\n", ret);
+ }
+
+ mOverlayW = w;
+ mOverlayH = h;
+
+ return ret;
+}
+
+status_t CameraService::Client::registerPreviewBuffers()
+{
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ params.getPreviewSize(&w, &h);
+
+ // don't use a hardcoded format here
+ ISurface::BufferHeap buffers(w, h, w, h,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP,
+ mOrientation,
+ 0,
+ mHardware->getPreviewHeap());
+
+ status_t ret = mSurface->registerBuffers(buffers);
+ if (ret != NO_ERROR) {
+ LOGE("registerBuffers failed with status %d", ret);
+ }
+ return ret;
+}
+
+status_t CameraService::Client::startPreviewMode()
+{
+ LOGV("startPreviewMode (pid %d)", getCallingPid());
+
+ // if preview has been enabled, nothing needs to be done
+ if (mHardware->previewEnabled()) {
+ return NO_ERROR;
+ }
+
+ // start preview mode
+#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
+ debug_frame_cnt = 0;
+#endif
+ status_t ret = NO_ERROR;
+
+ if (mUseOverlay) {
+ // If preview display has been set, set overlay now.
+ if (mSurface != 0) {
+ ret = setOverlay();
+ }
+ if (ret != NO_ERROR) return ret;
+ ret = mHardware->startPreview();
+ } else {
+ mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ ret = mHardware->startPreview();
+ if (ret != NO_ERROR) return ret;
+ // If preview display has been set, register preview buffers now.
+ if (mSurface != 0) {
+ // Unregister here because the surface registered with raw heap.
+ mSurface->unregisterBuffers();
+ ret = registerPreviewBuffers();
+ }
+ }
+ return ret;
+}
+
+status_t CameraService::Client::startPreview()
+{
+ LOGV("startPreview (pid %d)", getCallingPid());
+
+ return startCameraMode(CAMERA_PREVIEW_MODE);
+}
+
+status_t CameraService::Client::startRecording()
+{
+ LOGV("startRecording (pid %d)", getCallingPid());
+
+ if (mMediaPlayerBeep.get() != NULL) {
+ // do not play record jingle if stream volume is 0
+ // (typically because ringer mode is silent).
+ int index;
+ AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
+ if (index != 0) {
+ mMediaPlayerBeep->seekTo(0);
+ mMediaPlayerBeep->start();
+ }
+ }
+
+ mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
+
+ return startCameraMode(CAMERA_RECORDING_MODE);
+}
+
+// stop preview mode
+void CameraService::Client::stopPreview()
+{
+ LOGV("stopPreview (pid %d)", getCallingPid());
+
+ // hold main lock during state transition
+ {
+ Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return;
+ }
+
+ mHardware->stopPreview();
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ LOGV("stopPreview(), hardware stopped OK");
+
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->unregisterBuffers();
+ }
+ }
+
+ // hold preview buffer lock
+ {
+ Mutex::Autolock lock(mPreviewLock);
+ mPreviewBuffer.clear();
+ }
+}
+
+// stop recording mode
+void CameraService::Client::stopRecording()
+{
+ LOGV("stopRecording (pid %d)", getCallingPid());
+
+ // hold main lock during state transition
+ {
+ Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return;
+ }
+
+ if (mMediaPlayerBeep.get() != NULL) {
+ mMediaPlayerBeep->seekTo(0);
+ mMediaPlayerBeep->start();
+ }
+
+ mHardware->stopRecording();
+ mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+ LOGV("stopRecording(), hardware stopped OK");
+ }
+
+ // hold preview buffer lock
+ {
+ Mutex::Autolock lock(mPreviewLock);
+ mPreviewBuffer.clear();
+ }
+}
+
+// release a recording frame
+void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem)
+{
+ Mutex::Autolock lock(mLock);
+ if (checkPid() != NO_ERROR) return;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return;
+ }
+
+ mHardware->releaseRecordingFrame(mem);
+}
+
+bool CameraService::Client::previewEnabled()
+{
+ Mutex::Autolock lock(mLock);
+ if (mHardware == 0) return false;
+ return mHardware->previewEnabled();
+}
+
+bool CameraService::Client::recordingEnabled()
+{
+ Mutex::Autolock lock(mLock);
+ if (mHardware == 0) return false;
+ return mHardware->recordingEnabled();
+}
+
+// Safely retrieves a strong pointer to the client during a hardware callback.
+sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user)
+{
+ sp<Client> client = 0;
+ CameraService *service = static_cast<CameraService*>(user);
+ if (service != NULL) {
+ Mutex::Autolock ourLock(service->mServiceLock);
+ if (service->mClient != 0) {
+ client = service->mClient.promote();
+ if (client == 0) {
+ LOGE("getClientFromCookie: client appears to have died");
+ service->mClient.clear();
+ }
+ } else {
+ LOGE("getClientFromCookie: got callback but client was NULL");
+ }
+ }
+ return client;
+}
+
+
+#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \
+ DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \
+ DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
+static void dump_to_file(const char *fname,
+ uint8_t *buf, uint32_t size)
+{
+ int nw, cnt = 0;
+ uint32_t written = 0;
+
+ LOGV("opening file [%s]\n", fname);
+ int fd = open(fname, O_RDWR | O_CREAT);
+ if (fd < 0) {
+ LOGE("failed to create file [%s]: %s", fname, strerror(errno));
+ return;
+ }
+
+ LOGV("writing %d bytes to file [%s]\n", size, fname);
+ while (written < size) {
+ nw = ::write(fd,
+ buf + written,
+ size - written);
+ if (nw < 0) {
+ LOGE("failed to write to file [%s]: %s",
+ fname, strerror(errno));
+ break;
+ }
+ written += nw;
+ cnt++;
+ }
+ LOGV("done writing %d bytes to file [%s] in %d passes\n",
+ size, fname, cnt);
+ ::close(fd);
+}
+#endif
+
+status_t CameraService::Client::autoFocus()
+{
+ LOGV("autoFocus (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ return mHardware->autoFocus();
+}
+
+status_t CameraService::Client::cancelAutoFocus()
+{
+ LOGV("cancelAutoFocus (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ return mHardware->cancelAutoFocus();
+}
+
+// take a picture - image is returned in callback
+status_t CameraService::Client::takePicture()
+{
+ LOGV("takePicture (pid %d)", getCallingPid());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ mHardware->enableMsgType(CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+
+ return mHardware->takePicture();
+}
+
+// snapshot taken
+void CameraService::Client::handleShutter(
+ image_rect_type *size // The width and height of yuv picture for
+ // registerBuffer. If this is NULL, use the picture
+ // size from parameters.
+)
+{
+ // Play shutter sound.
+ if (mMediaPlayerClick.get() != NULL) {
+ // do not play shutter sound if stream volume is 0
+ // (typically because ringer mode is silent).
+ int index;
+ AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index);
+ if (index != 0) {
+ mMediaPlayerClick->seekTo(0);
+ mMediaPlayerClick->start();
+ }
+ }
+
+ // Screen goes black after the buffer is unregistered.
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->unregisterBuffers();
+ }
+
+ sp<ICameraClient> c = mCameraClient;
+ if (c != NULL) {
+ c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
+ }
+ mHardware->disableMsgType(CAMERA_MSG_SHUTTER);
+
+ // It takes some time before yuvPicture callback to be called.
+ // Register the buffer for raw image here to reduce latency.
+ if (mSurface != 0 && !mUseOverlay) {
+ int w, h;
+ CameraParameters params(mHardware->getParameters());
+ if (size == NULL) {
+ params.getPictureSize(&w, &h);
+ } else {
+ w = size->width;
+ h = size->height;
+ w &= ~1;
+ h &= ~1;
+ LOGV("Snapshot image width=%d, height=%d", w, h);
+ }
+ // FIXME: don't use hardcoded format constants here
+ ISurface::BufferHeap buffers(w, h, w, h,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0,
+ mHardware->getRawHeap());
+
+ mSurface->registerBuffers(buffers);
+ }
+}
+
+// preview callback - frame buffer update
+void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
+{
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+
+#if DEBUG_HEAP_LEAKS && 0 // debugging
+ if (gWeakHeap == NULL) {
+ if (gWeakHeap != heap) {
+ LOGV("SETTING PREVIEW HEAP");
+ heap->trackMe(true, true);
+ gWeakHeap = heap;
+ }
+ }
+#endif
+#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
+ {
+ if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
+ dump_to_file("/data/preview.yuv",
+ (uint8_t *)heap->base() + offset, size);
+ }
+ }
+#endif
+
+ if (!mUseOverlay)
+ {
+ Mutex::Autolock surfaceLock(mSurfaceLock);
+ if (mSurface != NULL) {
+ mSurface->postBuffer(offset);
+ }
+ }
+
+ // local copy of the callback flags
+ int flags = mPreviewCallbackFlag;
+
+ // is callback enabled?
+ if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
+ // If the enable bit is off, the copy-out and one-shot bits are ignored
+ LOGV("frame callback is diabled");
+ return;
+ }
+
+ // hold a strong pointer to the client
+ sp<ICameraClient> c = mCameraClient;
+
+ // clear callback flags if no client or one-shot mode
+ if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
+ LOGV("Disable preview callback");
+ mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
+ FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ // TODO: Shouldn't we use this API for non-overlay hardware as well?
+ if (mUseOverlay)
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
+
+ // Is the received frame copied out or not?
+ if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ LOGV("frame is copied");
+ copyFrameAndPostCopiedFrame(c, heap, offset, size);
+ } else {
+ LOGV("frame is forwarded");
+ c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
+ }
+}
+
+// picture callback - postview image ready
+void CameraService::Client::handlePostview(const sp<IMemory>& mem)
+{
+#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only
+ {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ dump_to_file("/data/postview.yuv",
+ (uint8_t *)heap->base() + offset, size);
+ }
+#endif
+
+ sp<ICameraClient> c = mCameraClient;
+ if (c != NULL) {
+ c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
+ }
+ mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
+}
+
+// picture callback - raw image ready
+void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
+{
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+#if DEBUG_HEAP_LEAKS && 0 // debugging
+ gWeakHeap = heap; // debugging
+#endif
+
+ //LOGV("handleRawPicture(%d, %d)", offset, size);
+#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only
+ dump_to_file("/data/photo.yuv",
+ (uint8_t *)heap->base() + offset, size);
+#endif
+
+ // Put the YUV version of the snapshot in the preview display.
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->postBuffer(offset);
+ }
+
+ sp<ICameraClient> c = mCameraClient;
+ if (c != NULL) {
+ c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
+ }
+ mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);
+}
+
+// picture callback - compressed picture ready
+void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)
+{
+#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
+ {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ dump_to_file("/data/photo.jpg",
+ (uint8_t *)heap->base() + offset, size);
+ }
+#endif
+
+ sp<ICameraClient> c = mCameraClient;
+ if (c != NULL) {
+ c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
+ }
+ mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
+}
+
+void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user)
+{
+ LOGV("notifyCallback(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) {
+ return;
+ }
+
+ switch (msgType) {
+ case CAMERA_MSG_SHUTTER:
+ // ext1 is the dimension of the yuv picture.
+ client->handleShutter((image_rect_type *)ext1);
+ break;
+ default:
+ sp<ICameraClient> c = client->mCameraClient;
+ if (c != NULL) {
+ c->notifyCallback(msgType, ext1, ext2);
+ }
+ break;
+ }
+
+#if DEBUG_CLIENT_REFERENCES
+ if (client->getStrongCount() == 1) {
+ LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!");
+ client->printRefs();
+ }
+#endif
+}
+
+void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
+{
+ LOGV("dataCallback(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) {
+ return;
+ }
+
+ sp<ICameraClient> c = client->mCameraClient;
+ if (dataPtr == NULL) {
+ LOGE("Null data returned in data callback");
+ if (c != NULL) {
+ c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ c->dataCallback(msgType, NULL);
+ }
+ return;
+ }
+
+ switch (msgType) {
+ case CAMERA_MSG_PREVIEW_FRAME:
+ client->handlePreviewData(dataPtr);
+ break;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ client->handlePostview(dataPtr);
+ break;
+ case CAMERA_MSG_RAW_IMAGE:
+ client->handleRawPicture(dataPtr);
+ break;
+ case CAMERA_MSG_COMPRESSED_IMAGE:
+ client->handleCompressedPicture(dataPtr);
+ break;
+ default:
+ if (c != NULL) {
+ c->dataCallback(msgType, dataPtr);
+ }
+ break;
+ }
+
+#if DEBUG_CLIENT_REFERENCES
+ if (client->getStrongCount() == 1) {
+ LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
+ client->printRefs();
+ }
+#endif
+}
+
+void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& dataPtr, void* user)
+{
+ LOGV("dataCallbackTimestamp(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) {
+ return;
+ }
+ sp<ICameraClient> c = client->mCameraClient;
+
+ if (dataPtr == NULL) {
+ LOGE("Null data returned in data with timestamp callback");
+ if (c != NULL) {
+ c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ c->dataCallbackTimestamp(0, msgType, NULL);
+ }
+ return;
+ }
+
+ if (c != NULL) {
+ c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
+ }
+
+#if DEBUG_CLIENT_REFERENCES
+ if (client->getStrongCount() == 1) {
+ LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!");
+ client->printRefs();
+ }
+#endif
+}
+
+// set preview/capture parameters - key/value pairs
+status_t CameraService::Client::setParameters(const String8& params)
+{
+ LOGV("setParameters(%s)", params.string());
+
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ CameraParameters p(params);
+
+ return mHardware->setParameters(p);
+}
+
+// get preview/capture parameters - key/value pairs
+String8 CameraService::Client::getParameters() const
+{
+ Mutex::Autolock lock(mLock);
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return String8();
+ }
+
+ String8 params(mHardware->getParameters().flatten());
+ LOGV("getParameters(%s)", params.string());
+ return params;
+}
+
+status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
+{
+ LOGV("sendCommand (pid %d)", getCallingPid());
+ Mutex::Autolock lock(mLock);
+ status_t result = checkPid();
+ if (result != NO_ERROR) return result;
+
+ if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {
+ // The orientation cannot be set during preview.
+ if (mHardware->previewEnabled()) {
+ return INVALID_OPERATION;
+ }
+ switch (arg1) {
+ case 0:
+ mOrientation = ISurface::BufferHeap::ROT_0;
+ break;
+ case 90:
+ mOrientation = ISurface::BufferHeap::ROT_90;
+ break;
+ case 180:
+ mOrientation = ISurface::BufferHeap::ROT_180;
+ break;
+ case 270:
+ mOrientation = ISurface::BufferHeap::ROT_270;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ return OK;
+ }
+
+ if (mHardware == 0) {
+ LOGE("mHardware is NULL, returning.");
+ return INVALID_OPERATION;
+ }
+
+ return mHardware->sendCommand(cmd, arg1, arg2);
+}
+
+void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
+ const sp<IMemoryHeap>& heap, size_t offset, size_t size)
+{
+ LOGV("copyFrameAndPostCopiedFrame");
+ // It is necessary to copy out of pmem before sending this to
+ // the callback. For efficiency, reuse the same MemoryHeapBase
+ // provided it's big enough. Don't allocate the memory or
+ // perform the copy if there's no callback.
+
+ // hold the preview lock while we grab a reference to the preview buffer
+ sp<MemoryHeapBase> previewBuffer;
+ {
+ Mutex::Autolock lock(mPreviewLock);
+ if (mPreviewBuffer == 0) {
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ } else if (size > mPreviewBuffer->virtualSize()) {
+ mPreviewBuffer.clear();
+ mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
+ }
+ if (mPreviewBuffer == 0) {
+ LOGE("failed to allocate space for preview buffer");
+ return;
+ }
+ previewBuffer = mPreviewBuffer;
+ }
+ memcpy(previewBuffer->base(),
+ (uint8_t *)heap->base() + offset, size);
+
+ sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
+ if (frame == 0) {
+ LOGE("failed to allocate space for frame callback");
+ return;
+ }
+ client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
+}
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 60000;
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t CameraService::dump(int fd, const Vector<String16>& args)
+{
+ static const char* kDeadlockedString = "CameraService may be deadlocked\n";
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump CameraService from pid=%d, uid=%d\n",
+ getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ } else {
+ bool locked = tryLock(mServiceLock);
+ // failed to lock - CameraService is probably deadlocked
+ if (!locked) {
+ String8 result(kDeadlockedString);
+ write(fd, result.string(), result.size());
+ }
+
+ if (mClient != 0) {
+ sp<Client> currentClient = mClient.promote();
+ sprintf(buffer, "Client (%p) PID: %d\n",
+ currentClient->getCameraClient()->asBinder().get(),
+ currentClient->mClientPid);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ currentClient->mHardware->dump(fd, args);
+ } else {
+ result.append("No camera client yet.\n");
+ write(fd, result.string(), result.size());
+ }
+
+ if (locked) mServiceLock.unlock();
+ }
+ return NO_ERROR;
+}
+
+
+status_t CameraService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ // permission checks...
+ switch (code) {
+ case BnCameraService::CONNECT:
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int self_pid = getpid();
+ if (pid != self_pid) {
+ // we're called from a different process, do the real check
+ if (!checkCallingPermission(
+ String16("android.permission.CAMERA")))
+ {
+ const int uid = ipc->getCallingUid();
+ LOGE("Permission Denial: "
+ "can't use the camera pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ }
+ break;
+ }
+
+ status_t err = BnCameraService::onTransact(code, data, reply, flags);
+
+#if DEBUG_HEAP_LEAKS
+ LOGV("+++ onTransact err %d code %d", err, code);
+
+ if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
+ // the 'service' command interrogates this binder for its name, and then supplies it
+ // even for the debugging commands. that means we need to check for it here, using
+ // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
+ // BnSurfaceComposer before falling through to this code).
+
+ LOGV("+++ onTransact code %d", code);
+
+ CHECK_INTERFACE(ICameraService, data, reply);
+
+ switch(code) {
+ case 1000:
+ {
+ if (gWeakHeap != 0) {
+ sp<IMemoryHeap> h = gWeakHeap.promote();
+ IMemoryHeap *p = gWeakHeap.unsafe_get();
+ LOGV("CHECKING WEAK REFERENCE %p (%p)", h.get(), p);
+ if (h != 0)
+ h->printRefs();
+ bool attempt_to_delete = data.readInt32() == 1;
+ if (attempt_to_delete) {
+ // NOT SAFE!
+ LOGV("DELETING WEAK REFERENCE %p (%p)", h.get(), p);
+ if (p) delete p;
+ }
+ return NO_ERROR;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+#endif // DEBUG_HEAP_LEAKS
+
+ return err;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
new file mode 100644
index 0000000..bc49b1d
--- /dev/null
+++ b/services/camera/libcameraservice/CameraService.h
@@ -0,0 +1,227 @@
+/*
+**
+** Copyright (C) 2008, The Android Open Source Project
+** Copyright (C) 2008 HTC Inc.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
+
+#include <camera/ICameraService.h>
+#include <camera/CameraHardwareInterface.h>
+#include <camera/Camera.h>
+
+namespace android {
+
+class MemoryHeapBase;
+class MediaPlayer;
+
+// ----------------------------------------------------------------------------
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+// When enabled, this feature allows you to send an event to the CameraService
+// so that you can cause all references to the heap object gWeakHeap, defined
+// below, to be printed. You will also need to set DEBUG_REFS=1 and
+// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp. You just have to
+// set gWeakHeap to the appropriate heap you want to track.
+
+#define DEBUG_HEAP_LEAKS 0
+
+// ----------------------------------------------------------------------------
+
+class CameraService : public BnCameraService
+{
+ class Client;
+
+public:
+ static void instantiate();
+
+ // ICameraService interface
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ void removeClient(const sp<ICameraClient>& cameraClient);
+
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+
+private:
+
+// ----------------------------------------------------------------------------
+
+ class Client : public BnCamera {
+
+ public:
+ virtual void disconnect();
+
+ // connect new client with existing camera remote
+ virtual status_t connect(const sp<ICameraClient>& client);
+
+ // prevent other processes from using this ICamera interface
+ virtual status_t lock();
+
+ // allow other processes to use this ICamera interface
+ virtual status_t unlock();
+
+ // pass the buffered ISurface to the camera service
+ virtual status_t setPreviewDisplay(const sp<ISurface>& surface);
+
+ // set the preview callback flag to affect how the received frames from
+ // preview are handled.
+ virtual void setPreviewCallbackFlag(int callback_flag);
+
+ // start preview mode, must call setPreviewDisplay first
+ virtual status_t startPreview();
+
+ // stop preview mode
+ virtual void stopPreview();
+
+ // get preview state
+ virtual bool previewEnabled();
+
+ // start recording mode
+ virtual status_t startRecording();
+
+ // stop recording mode
+ virtual void stopRecording();
+
+ // get recording state
+ virtual bool recordingEnabled();
+
+ // release a recording frame
+ virtual void releaseRecordingFrame(const sp<IMemory>& mem);
+
+ // auto focus
+ virtual status_t autoFocus();
+
+ // cancel auto focus
+ virtual status_t cancelAutoFocus();
+
+ // take a picture - returns an IMemory (ref-counted mmap)
+ virtual status_t takePicture();
+
+ // set preview/capture parameters - key/value pairs
+ virtual status_t setParameters(const String8& params);
+
+ // get preview/capture parameters - key/value pairs
+ virtual String8 getParameters() const;
+
+ // send command to camera driver
+ virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
+
+ // our client...
+ const sp<ICameraClient>& getCameraClient() const { return mCameraClient; }
+
+ private:
+ friend class CameraService;
+ Client(const sp<CameraService>& cameraService,
+ const sp<ICameraClient>& cameraClient,
+ pid_t clientPid);
+ Client();
+ virtual ~Client();
+
+ status_t checkPid();
+
+ static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
+ static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& dataPtr, void* user);
+
+ static sp<Client> getClientFromCookie(void* user);
+
+ void handlePreviewData(const sp<IMemory>&);
+ void handleShutter(image_rect_type *image);
+ void handlePostview(const sp<IMemory>&);
+ void handleRawPicture(const sp<IMemory>&);
+ void handleCompressedPicture(const sp<IMemory>&);
+
+ void copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
+ const sp<IMemoryHeap>& heap, size_t offset, size_t size);
+
+ // camera operation mode
+ enum camera_mode {
+ CAMERA_PREVIEW_MODE = 0, // frame automatically released
+ CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame()
+ };
+ status_t startCameraMode(camera_mode mode);
+ status_t startPreviewMode();
+ status_t startRecordingMode();
+ status_t setOverlay();
+ status_t registerPreviewBuffers();
+
+ // Ensures atomicity among the public methods
+ mutable Mutex mLock;
+
+ // mSurfaceLock synchronizes access to mSurface between
+ // setPreviewSurface() and postPreviewFrame(). Note that among
+ // the public methods, all accesses to mSurface are
+ // syncrhonized by mLock. However, postPreviewFrame() is called
+ // by the CameraHardwareInterface callback, and needs to
+ // access mSurface. It cannot hold mLock, however, because
+ // stopPreview() may be holding that lock while attempting
+ // to stop preview, and stopPreview itself will block waiting
+ // for a callback from CameraHardwareInterface. If this
+ // happens, it will cause a deadlock.
+ mutable Mutex mSurfaceLock;
+ mutable Condition mReady;
+ sp<CameraService> mCameraService;
+ sp<ISurface> mSurface;
+ int mPreviewCallbackFlag;
+ int mOrientation;
+
+ sp<MediaPlayer> mMediaPlayerClick;
+ sp<MediaPlayer> mMediaPlayerBeep;
+
+ // these are immutable once the object is created,
+ // they don't need to be protected by a lock
+ sp<ICameraClient> mCameraClient;
+ sp<CameraHardwareInterface> mHardware;
+ pid_t mClientPid;
+ bool mUseOverlay;
+
+ sp<OverlayRef> mOverlayRef;
+ int mOverlayW;
+ int mOverlayH;
+
+ mutable Mutex mPreviewLock;
+ sp<MemoryHeapBase> mPreviewBuffer;
+ };
+
+// ----------------------------------------------------------------------------
+
+ CameraService();
+ virtual ~CameraService();
+
+ // We use a count for number of clients (shoule only be 0 or 1).
+ volatile int32_t mUsers;
+ virtual void incUsers();
+ virtual void decUsers();
+
+ mutable Mutex mServiceLock;
+ wp<Client> mClient;
+
+#if DEBUG_HEAP_LEAKS
+ wp<IMemoryHeap> gWeakHeap;
+#endif
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h
new file mode 100644
index 0000000..b6266fb
--- /dev/null
+++ b/services/camera/libcameraservice/CannedJpeg.h
@@ -0,0 +1,734 @@
+const int kCannedJpegWidth = 320;
+const int kCannedJpegHeight = 240;
+const int kCannedJpegSize = 8733;
+
+const char kCannedJpeg[] = {
+ 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
+ 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66,
+ 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x1a, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x3e, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x46, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x31, 0x01, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33,
+ 0x2e, 0x33, 0x36, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02,
+ 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05,
+ 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, 0x08, 0x0c,
+ 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d,
+ 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15,
+ 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15,
+ 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05,
+ 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0,
+ 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02,
+ 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01,
+ 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03,
+ 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13,
+ 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23,
+ 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82,
+ 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00,
+ 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+ 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1,
+ 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
+ 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+ 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00,
+ 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xd2, 0xa3, 0x95, 0xbb,
+ 0x54, 0x84, 0xe0, 0x66, 0xa0, 0x27, 0x27, 0x35, 0xed, 0x9e, 0x50, 0x95,
+ 0x2c, 0x4b, 0xc6, 0x6a, 0x35, 0x1b, 0x8e, 0x2a, 0x70, 0x30, 0x28, 0x00,
+ 0xa8, 0xe5, 0x6e, 0x71, 0x52, 0x31, 0xda, 0x33, 0x50, 0x13, 0x93, 0x40,
+ 0x09, 0x52, 0xc6, 0xb8, 0x19, 0xf5, 0xa6, 0x2a, 0xee, 0x6c, 0x54, 0xd4,
+ 0x00, 0x54, 0x52, 0x36, 0x5b, 0x1e, 0x95, 0x23, 0xb6, 0xd5, 0xcd, 0x41,
+ 0x40, 0x05, 0x4c, 0x8b, 0xb5, 0x7d, 0xea, 0x34, 0x5d, 0xcd, 0xed, 0x53,
+ 0x50, 0x01, 0x50, 0xbb, 0x6e, 0x6f, 0x6a, 0x91, 0xdb, 0x6a, 0xfb, 0xd4,
+ 0x34, 0x00, 0x54, 0xe8, 0xbb, 0x57, 0x15, 0x1c, 0x6b, 0x96, 0xcf, 0xa5,
+ 0x4b, 0x40, 0x05, 0x42, 0xcd, 0xb9, 0xb3, 0x4f, 0x91, 0xb0, 0x31, 0xeb,
+ 0x51, 0x50, 0x02, 0x81, 0x93, 0x53, 0xa8, 0xda, 0x31, 0x51, 0xc4, 0xbc,
+ 0xe6, 0xa4, 0xa0, 0x00, 0x9c, 0x0a, 0x81, 0x8e, 0xe3, 0x9a, 0x92, 0x56,
+ 0xe3, 0x15, 0x15, 0x00, 0x28, 0x19, 0x38, 0xa9, 0xc0, 0xc0, 0xc5, 0x47,
+ 0x12, 0xf7, 0xa9, 0x28, 0x00, 0x27, 0x00, 0x9a, 0x80, 0x9c, 0x9c, 0xd3,
+ 0xe5, 0x6e, 0xd5, 0x1d, 0x00, 0x2a, 0x8d, 0xc7, 0x15, 0x3d, 0x32, 0x35,
+ 0xc0, 0xcf, 0xad, 0x3e, 0x80, 0x11, 0x8e, 0xd1, 0x9a, 0x82, 0x9f, 0x23,
+ 0x64, 0xe3, 0xd2, 0x99, 0x40, 0x0e, 0x45, 0xdc, 0xde, 0xd5, 0x35, 0x36,
+ 0x35, 0xc2, 0xfb, 0x9a, 0x75, 0x00, 0x35, 0xdb, 0x6a, 0xfb, 0xd4, 0x34,
+ 0xe9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, 0x00, 0x3e, 0x35, 0xcb, 0x7b, 0x0a,
+ 0x96, 0x91, 0x17, 0x6a, 0xd2, 0xd0, 0x03, 0x64, 0x6c, 0x2f, 0xb9, 0xa8,
+ 0x69, 0xce, 0xdb, 0x9a, 0x9b, 0xd6, 0x80, 0x1f, 0x12, 0xe4, 0xe7, 0xd2,
+ 0xa5, 0xa4, 0x51, 0xb4, 0x62, 0x97, 0xa5, 0x00, 0x67, 0xc9, 0xad, 0xd8,
+ 0x91, 0x81, 0x72, 0x9f, 0x9d, 0x47, 0xfd, 0xb3, 0x65, 0xff, 0x00, 0x3f,
+ 0x29, 0x5f, 0xa0, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad,
+ 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43,
+ 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd7, 0x3f, 0xb7, 0x87, 0x73, 0x6f,
+ 0x63, 0x33, 0xe0, 0x28, 0xf5, 0x9b, 0x11, 0xc9, 0xb9, 0x4c, 0xfd, 0x69,
+ 0xff, 0x00, 0xdb, 0x96, 0x1f, 0xf3, 0xf5, 0x1f, 0xe7, 0x5f, 0x7d, 0x7f,
+ 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80,
+ 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7,
+ 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, 0x02, 0x93,
+ 0x5b, 0xb1, 0x3c, 0x0b, 0x94, 0xc7, 0xd6, 0x99, 0xfd, 0xb3, 0x65, 0xff,
+ 0x00, 0x3f, 0x29, 0xf9, 0xd7, 0xe8, 0x07, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f,
+ 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09,
+ 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0xbd, 0xbc, 0x03, 0xd8,
+ 0xcc, 0xf8, 0x0e, 0x3d, 0x6a, 0xc1, 0x47, 0x37, 0x29, 0x9f, 0xad, 0x3b,
+ 0xfb, 0x72, 0xc3, 0xfe, 0x7e, 0xa3, 0xfc, 0xeb, 0xef, 0xaf, 0xf8, 0x74,
+ 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1,
+ 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48,
+ 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x19, 0x35, 0xbb, 0x26,
+ 0x3c, 0x5c, 0xa6, 0x3e, 0xb4, 0xdf, 0xed, 0x9b, 0x2f, 0xf9, 0xf9, 0x4a,
+ 0xfd, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
+ 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
+ 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x97, 0xb7, 0x80, 0x7b, 0x19, 0x9f, 0x01,
+ 0xa6, 0xb5, 0x60, 0xab, 0xff, 0x00, 0x1f, 0x51, 0xe7, 0xeb, 0x4e, 0xfe,
+ 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0xfb, 0xeb, 0xfe,
+ 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f,
+ 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a,
+ 0x3d, 0xbc, 0x03, 0xd8, 0xcc, 0xf8, 0x05, 0xf5, 0xab, 0x26, 0x6f, 0xf8,
+ 0xf9, 0x4c, 0x7d, 0x69, 0xbf, 0xdb, 0x36, 0x5f, 0xf3, 0xf2, 0x9f, 0x9d,
+ 0x7e, 0x80, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5,
+ 0xff, 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd,
+ 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x80, 0x7b, 0x19, 0x9f,
+ 0x02, 0x26, 0xb5, 0x60, 0xab, 0x8f, 0xb5, 0x47, 0xf9, 0xd2, 0xff, 0x00,
+ 0x6e, 0x58, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x7d, 0xf5, 0xff, 0x00, 0x0e,
+ 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc,
+ 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e,
+ 0xde, 0x01, 0xec, 0x66, 0x7c, 0x00, 0xda, 0xd5, 0x93, 0x1c, 0xfd, 0xa5,
+ 0x3f, 0x3a, 0x4f, 0xed, 0x8b, 0x2f, 0xf9, 0xf9, 0x4f, 0xce, 0xbf, 0x40,
+ 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00,
+ 0xc0, 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd,
+ 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa7, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
+ 0x81, 0x57, 0x5a, 0xb0, 0x51, 0x8f, 0xb5, 0x47, 0xf9, 0xd1, 0xfd, 0xb9,
+ 0x61, 0xff, 0x00, 0x3f, 0x49, 0xf9, 0xd7, 0xdf, 0x5f, 0xf0, 0xe9, 0x6f,
+ 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5,
+ 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd2,
+ 0xf6, 0xf0, 0x0f, 0x63, 0x33, 0xe0, 0x06, 0xd6, 0xac, 0x98, 0xe7, 0xed,
+ 0x29, 0xf9, 0xd2, 0x0d, 0x62, 0xcb, 0xfe, 0x7e, 0x53, 0xf3, 0xaf, 0xd0,
+ 0x0f, 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12,
+ 0x1a, 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff,
+ 0x00, 0xc0, 0x48, 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x51,
+ 0xad, 0xd8, 0x01, 0x8f, 0xb5, 0x47, 0xf9, 0xd0, 0x75, 0xcb, 0x0c, 0x7f,
+ 0xc7, 0xca, 0x7e, 0x75, 0xf7, 0xd7, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, 0xf4,
+ 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f,
+ 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7b, 0x78, 0x77, 0x0f, 0x63,
+ 0x33, 0xf3, 0xfc, 0xeb, 0x36, 0x44, 0xff, 0x00, 0xc7, 0xca, 0x7e, 0x74,
+ 0xa3, 0x58, 0xb1, 0x24, 0x66, 0xe5, 0x31, 0xf5, 0xaf, 0xbf, 0xff, 0x00,
+ 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0,
+ 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
+ 0xff, 0x00, 0x01, 0x21, 0xa3, 0xdb, 0xc3, 0xb8, 0x7b, 0x19, 0x9f, 0x02,
+ 0xff, 0x00, 0x6d, 0xd8, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x07, 0x5c, 0xb1,
+ 0x03, 0x8b, 0x94, 0xcf, 0xd6, 0xbe, 0xfa, 0xff, 0x00, 0x87, 0x4b, 0x78,
+ 0x4b, 0xfe, 0x87, 0xdd, 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d,
+ 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f,
+ 0x0e, 0xe1, 0xec, 0x66, 0x7e, 0x7f, 0xff, 0x00, 0x6c, 0xd9, 0x7f, 0xcf,
+ 0xca, 0x7e, 0x74, 0xab, 0xac, 0x58, 0xe7, 0x9b, 0x94, 0xc7, 0xd6, 0xbe,
+ 0xff, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b,
+ 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f,
+ 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, 0x0e, 0xe1, 0xec, 0x66, 0x7c,
+ 0x0b, 0xfd, 0xb9, 0x61, 0xff, 0x00, 0x3f, 0x51, 0xfe, 0x74, 0x8d, 0xae,
+ 0x58, 0xed, 0x38, 0xb9, 0x4c, 0xfd, 0x6b, 0xef, 0xbf, 0xf8, 0x74, 0xb7,
+ 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, 0xd2,
+ 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, 0x68,
+ 0xf6, 0xf0, 0xee, 0x1e, 0xc6, 0x67, 0xe7, 0xff, 0x00, 0xf6, 0xc5, 0x97,
+ 0xfc, 0xfc, 0xa7, 0xe7, 0x4e, 0x4d, 0x62, 0xc7, 0x77, 0x37, 0x29, 0xf9,
+ 0xd7, 0xdf, 0xdf, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f,
+ 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee,
+ 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf,
+ 0x81, 0x7f, 0xb7, 0x2c, 0x3f, 0xe7, 0xea, 0x3f, 0xce, 0x91, 0xf5, 0xcb,
+ 0x1c, 0x71, 0x72, 0x9f, 0x9d, 0x7d, 0xf7, 0xff, 0x00, 0x0e, 0x96, 0xf0,
+ 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, 0x3a, 0x5b,
+ 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, 0xde, 0x1d,
+ 0xc3, 0xd8, 0xcc, 0xfc, 0xff, 0x00, 0xfe, 0xd9, 0xb2, 0xff, 0x00, 0x9f,
+ 0x94, 0xfc, 0xe9, 0xd1, 0xeb, 0x36, 0x20, 0xe4, 0xdc, 0xa7, 0xe7, 0x5f,
+ 0x7f, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff,
+ 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f,
+ 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e,
+ 0x05, 0xfe, 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0x6c,
+ 0x9a, 0xdd, 0x89, 0x18, 0x17, 0x29, 0xf9, 0xd7, 0xdf, 0x9f, 0xf0, 0xe9,
+ 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3,
+ 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90,
+ 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, 0xbc, 0xa8, 0xa2, 0x8a, 0xf3,
+ 0x0e, 0xf0, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
+ 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
+ 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0xa0, 0xbb, 0xbd, 0xb7, 0xb0,
+ 0x88, 0x49, 0x73, 0x3c, 0x56, 0xf1, 0x96, 0x0a, 0x1e, 0x57, 0x0a, 0x09,
+ 0x3d, 0x06, 0x4f, 0x7a, 0x9e, 0x95, 0xd3, 0x76, 0xea, 0x01, 0x45, 0x14,
+ 0x53, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x82, 0xda, 0xf6, 0xde,
+ 0xf0, 0xca, 0x2d, 0xe7, 0x8a, 0x73, 0x13, 0x98, 0xe4, 0xf2, 0xdc, 0x36,
+ 0xc6, 0x1d, 0x54, 0xe3, 0xa1, 0xf6, 0xa4, 0xda, 0x4e, 0xcc, 0x09, 0xe8,
+ 0xa2, 0x8a, 0x60, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
+ 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14,
+ 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x02, 0xb8,
+ 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
+ 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x18, 0x51, 0x45, 0x14, 0x0a, 0xe1, 0x45,
+ 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb9, 0xca, 0xfc, 0x4a, 0xf0,
+ 0x52, 0x78, 0xef, 0xc2, 0xb7, 0x1a, 0x76, 0xef, 0x2e, 0xe5, 0x4f, 0x9d,
+ 0x6c, 0xe4, 0xe0, 0x09, 0x00, 0x38, 0xcf, 0xb1, 0xc9, 0x1f, 0x8e, 0x7b,
+ 0x57, 0x3d, 0xf0, 0x5b, 0xc7, 0x53, 0x6b, 0xba, 0x6c, 0xda, 0x16, 0xaa,
+ 0x5a, 0x3d, 0x73, 0x4a, 0xfd, 0xd4, 0x8b, 0x2f, 0xdf, 0x91, 0x01, 0xc0,
+ 0x27, 0xdc, 0x1e, 0x0f, 0xe0, 0x7b, 0xd7, 0xa3, 0x5c, 0xdc, 0xc5, 0x67,
+ 0x04, 0x93, 0xcf, 0x2a, 0x43, 0x0c, 0x60, 0xb3, 0xc9, 0x23, 0x05, 0x55,
+ 0x1e, 0xa4, 0x9e, 0x95, 0xf3, 0x47, 0xc4, 0x8f, 0x1f, 0xe9, 0x36, 0xdf,
+ 0x10, 0xed, 0x3c, 0x41, 0xe1, 0x39, 0x99, 0xaf, 0xa1, 0xe2, 0xea, 0x42,
+ 0x98, 0x82, 0x72, 0x38, 0xe3, 0x90, 0x4e, 0x46, 0x41, 0xe9, 0x9c, 0x0c,
+ 0x7a, 0xd7, 0xc4, 0x67, 0x98, 0x9a, 0x59, 0x3e, 0x26, 0x9e, 0x64, 0xa6,
+ 0x93, 0x7e, 0xec, 0xe3, 0x7d, 0x65, 0x1e, 0xe9, 0x77, 0x8b, 0xd7, 0xd3,
+ 0x4b, 0x99, 0x4d, 0xa8, 0xbe, 0x63, 0xe9, 0xca, 0x2b, 0xe4, 0x3d, 0x73,
+ 0xe3, 0x3f, 0x8b, 0xb5, 0xc6, 0x6d, 0xfa, 0xb4, 0x96, 0x71, 0x9e, 0x91,
+ 0x59, 0x0f, 0x28, 0x0f, 0xc4, 0x7c, 0xdf, 0x99, 0xae, 0x56, 0xe7, 0x5a,
+ 0xd4, 0x6f, 0x18, 0xb5, 0xc5, 0xfd, 0xd4, 0xec, 0x7b, 0xc9, 0x33, 0x31,
+ 0xfd, 0x4d, 0x78, 0x75, 0xf8, 0xfb, 0x0b, 0x07, 0x6a, 0x14, 0x65, 0x25,
+ 0xe6, 0xd2, 0xff, 0x00, 0x32, 0x1d, 0x75, 0xd1, 0x1f, 0x73, 0x51, 0x5f,
+ 0x0b, 0xdb, 0xea, 0xf7, 0xf6, 0xad, 0xba, 0x0b, 0xdb, 0x88, 0x58, 0x77,
+ 0x8e, 0x56, 0x53, 0xfa, 0x1a, 0xe9, 0xf4, 0x5f, 0x8b, 0xfe, 0x2e, 0xd0,
+ 0xd9, 0x7c, 0xad, 0x66, 0x7b, 0x84, 0x1f, 0xf2, 0xce, 0xec, 0xf9, 0xc0,
+ 0xff, 0x00, 0xdf, 0x59, 0x23, 0xf0, 0x34, 0xa8, 0x71, 0xf6, 0x1a, 0x4e,
+ 0xd5, 0xa8, 0x4a, 0x2b, 0xc9, 0xa7, 0xfe, 0x40, 0xab, 0xae, 0xa8, 0xfa,
+ 0x13, 0xe2, 0xff, 0x00, 0x8f, 0xcf, 0x82, 0xfc, 0x3e, 0x21, 0xb3, 0x6d,
+ 0xda, 0xcd, 0xfe, 0x62, 0xb5, 0x45, 0xe5, 0x97, 0xb1, 0x7c, 0x7b, 0x67,
+ 0x8f, 0x72, 0x3d, 0xea, 0x5f, 0x84, 0x7e, 0x05, 0x6f, 0x04, 0x78, 0x60,
+ 0x2d, 0xd1, 0x2d, 0xa9, 0xde, 0xb0, 0x9e, 0xe8, 0x93, 0x9d, 0xad, 0x8e,
+ 0x17, 0xf0, 0x1d, 0x4f, 0xa9, 0x35, 0xe2, 0x5e, 0x13, 0xf8, 0x89, 0x61,
+ 0xac, 0xfc, 0x49, 0x8f, 0xc4, 0x3e, 0x30, 0x76, 0xcc, 0x68, 0x16, 0xd8,
+ 0x43, 0x19, 0x68, 0x61, 0x61, 0xd0, 0x91, 0x92, 0x40, 0x1c, 0x9e, 0x33,
+ 0xc9, 0xcd, 0x7d, 0x3b, 0x63, 0x7f, 0x6d, 0xaa, 0x5a, 0x45, 0x75, 0x69,
+ 0x3c, 0x77, 0x36, 0xd2, 0x8d, 0xc9, 0x2c, 0x4c, 0x19, 0x58, 0x7b, 0x11,
+ 0x5e, 0xde, 0x4d, 0x8b, 0xa3, 0x9d, 0xe3, 0x2a, 0x66, 0x1c, 0xe9, 0xf2,
+ 0x5e, 0x30, 0x8f, 0x58, 0xae, 0xb2, 0x6b, 0xbc, 0xbf, 0x05, 0xa1, 0x50,
+ 0x6a, 0x6f, 0x98, 0xb1, 0x45, 0x14, 0x57, 0xdc, 0x9b, 0x5c, 0x28, 0xa2,
+ 0x8a, 0x02, 0xe1, 0x45, 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb8,
+ 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45,
+ 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x0b, 0x8d, 0xcd, 0x19, 0xa6, 0xe4, 0x51,
+ 0x91, 0x55, 0x62, 0x47, 0x66, 0x8c, 0xd3, 0x72, 0x28, 0xc8, 0xa2, 0xc0,
+ 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x01, 0xd9, 0xa3, 0x34,
+ 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, 0xcd, 0x19, 0xa6, 0xe4, 0x52, 0xe4,
+ 0x51, 0x60, 0xb8, 0xb9, 0xa3, 0x34, 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e,
+ 0xdd, 0x46, 0x69, 0xb9, 0x14, 0x64, 0x51, 0x60, 0x1d, 0x9a, 0xa7, 0xac,
+ 0x6b, 0x16, 0x9a, 0x0e, 0x9b, 0x71, 0xa8, 0x5f, 0x4c, 0x20, 0xb5, 0x81,
+ 0x37, 0xbb, 0x9e, 0xc3, 0xd0, 0x7a, 0x93, 0xd0, 0x0a, 0xb5, 0x91, 0x5f,
+ 0x39, 0xfe, 0xd1, 0x1e, 0x37, 0x7d, 0x4b, 0x5a, 0x4f, 0x0f, 0x5b, 0x48,
+ 0x45, 0xa5, 0x96, 0x1e, 0x70, 0xa7, 0xef, 0xca, 0x46, 0x40, 0x3f, 0xee,
+ 0x83, 0xf9, 0x93, 0xe9, 0x5e, 0x06, 0x79, 0x9a, 0xc3, 0x27, 0xc1, 0x4b,
+ 0x12, 0xd5, 0xe5, 0xb4, 0x57, 0x76, 0xff, 0x00, 0xab, 0xbf, 0x24, 0x44,
+ 0xe5, 0xca, 0xae, 0x72, 0xbf, 0x12, 0xbe, 0x2a, 0xea, 0x3e, 0x3e, 0xbd,
+ 0x78, 0xd5, 0x9e, 0xd3, 0x48, 0x46, 0xfd, 0xd5, 0xa2, 0x9f, 0xbd, 0xe8,
+ 0xcf, 0xea, 0x7f, 0x41, 0xdb, 0xd4, 0xc3, 0xe0, 0x5f, 0x85, 0x1a, 0xd7,
+ 0x8f, 0xed, 0xe6, 0xb9, 0xb1, 0xf2, 0x2d, 0xed, 0x22, 0x6d, 0x86, 0x7b,
+ 0x96, 0x21, 0x59, 0xb1, 0x9c, 0x0c, 0x02, 0x4f, 0x51, 0xf9, 0xd7, 0x19,
+ 0x5e, 0xcd, 0xf0, 0x73, 0xe3, 0x16, 0x97, 0xe1, 0x0d, 0x06, 0x4d, 0x23,
+ 0x57, 0x49, 0x63, 0x44, 0x95, 0xa5, 0x86, 0x78, 0x53, 0x78, 0x21, 0xba,
+ 0xab, 0x0e, 0xb9, 0xcf, 0x7f, 0x7f, 0x6a, 0xfc, 0x1b, 0x2e, 0xa9, 0x87,
+ 0xcd, 0xb3, 0x2f, 0x69, 0x9c, 0xd5, 0x6a, 0x2e, 0xfa, 0xde, 0xda, 0xf4,
+ 0x57, 0xe8, 0xbf, 0xe1, 0x8e, 0x48, 0xda, 0x52, 0xf7, 0x8f, 0x30, 0xf1,
+ 0x57, 0x85, 0x75, 0x0f, 0x06, 0xeb, 0x12, 0x69, 0xba, 0x94, 0x42, 0x3b,
+ 0x84, 0x01, 0x83, 0x21, 0xca, 0xba, 0x9e, 0x8c, 0xa7, 0xb8, 0xac, 0x8a,
+ 0xed, 0x3e, 0x2c, 0xf8, 0xee, 0x1f, 0x1f, 0xf8, 0x9c, 0x5e, 0xda, 0xc2,
+ 0xf0, 0xda, 0x41, 0x08, 0x82, 0x2f, 0x33, 0x01, 0xd8, 0x02, 0x49, 0x63,
+ 0xe9, 0xc9, 0x3c, 0x57, 0x17, 0x5e, 0x26, 0x3e, 0x9e, 0x1e, 0x96, 0x2a,
+ 0xa4, 0x30, 0xb2, 0xe6, 0xa6, 0x9b, 0xb3, 0xee, 0x88, 0x76, 0xbe, 0x81,
+ 0x5a, 0x1a, 0x06, 0x83, 0x7b, 0xe2, 0x7d, 0x5e, 0xdf, 0x4d, 0xd3, 0xe2,
+ 0xf3, 0xae, 0xa7, 0x38, 0x55, 0xce, 0x00, 0x00, 0x64, 0x92, 0x7b, 0x00,
+ 0x39, 0xac, 0xfa, 0xea, 0x3e, 0x1b, 0x78, 0xc1, 0x7c, 0x0d, 0xe2, 0xcb,
+ 0x5d, 0x52, 0x58, 0x4c, 0xf6, 0xe1, 0x5a, 0x39, 0x51, 0x3e, 0xf6, 0xd6,
+ 0x18, 0x24, 0x7b, 0x8e, 0x0d, 0x67, 0x83, 0x85, 0x1a, 0x98, 0x8a, 0x70,
+ 0xc4, 0x4b, 0x96, 0x0d, 0xae, 0x67, 0xd9, 0x5f, 0x50, 0x56, 0xbe, 0xa6,
+ 0x97, 0x8d, 0x7e, 0x0e, 0xeb, 0xde, 0x06, 0xd3, 0x17, 0x50, 0xbb, 0x36,
+ 0xf7, 0x56, 0x99, 0x0b, 0x24, 0x96, 0xae, 0x4f, 0x96, 0x4f, 0x4d, 0xc0,
+ 0x81, 0xc1, 0x3c, 0x66, 0xa9, 0xfc, 0x3e, 0xf8, 0x93, 0xaa, 0x78, 0x03,
+ 0x50, 0x0f, 0x6c, 0xe6, 0x7b, 0x07, 0x6f, 0xdf, 0xd9, 0x3b, 0x7c, 0x8e,
+ 0x3d, 0x47, 0xa3, 0x7b, 0xfe, 0x79, 0xaf, 0x45, 0xf8, 0xad, 0xf1, 0xb3,
+ 0x47, 0xf1, 0x27, 0x85, 0x26, 0xd2, 0x34, 0x84, 0x9a, 0x67, 0xbb, 0x2b,
+ 0xe6, 0xcb, 0x34, 0x7b, 0x04, 0x6a, 0x18, 0x36, 0x07, 0xa9, 0xc8, 0x1e,
+ 0xd5, 0xe1, 0x95, 0xf4, 0x39, 0xab, 0xc2, 0x65, 0x79, 0x84, 0x67, 0x93,
+ 0x55, 0x6d, 0x24, 0x9d, 0xd3, 0xbd, 0x9f, 0x55, 0x7e, 0xaa, 0xd6, 0xbe,
+ 0xfb, 0xd8, 0xb9, 0x5a, 0x32, 0xf7, 0x59, 0xf6, 0xef, 0x86, 0xbc, 0x49,
+ 0x63, 0xe2, 0xbd, 0x1a, 0xdf, 0x53, 0xd3, 0xe5, 0xf3, 0x2d, 0xe6, 0x1d,
+ 0xfe, 0xf2, 0x1e, 0xea, 0xc3, 0xb1, 0x15, 0xa9, 0x9a, 0xf9, 0x7b, 0xe0,
+ 0x27, 0x8d, 0xe4, 0xf0, 0xef, 0x8a, 0x53, 0x4a, 0x9e, 0x43, 0xfd, 0x9f,
+ 0xa9, 0x30, 0x8f, 0x69, 0x3c, 0x24, 0xdf, 0xc0, 0xc3, 0xeb, 0xf7, 0x7f,
+ 0x11, 0xe9, 0x5f, 0x4f, 0xe4, 0x57, 0xee, 0x3c, 0x3f, 0x9b, 0xc7, 0x39,
+ 0xc1, 0x2a, 0xed, 0x5a, 0x6b, 0x49, 0x2f, 0x3f, 0xf2, 0x7b, 0xfe, 0x1d,
+ 0x0e, 0xb8, 0x4f, 0x99, 0x5c, 0x76, 0x4d, 0x19, 0xa6, 0xe4, 0x51, 0x91,
+ 0x5f, 0x4b, 0x62, 0xc7, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
+ 0x2e, 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x0b, 0x8e, 0xcd,
+ 0x19, 0xa6, 0xe4, 0x51, 0x91, 0x45, 0x80, 0x76, 0x68, 0xcd, 0x37, 0x34,
+ 0x64, 0x51, 0x60, 0xb8, 0xec, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58,
+ 0x07, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, 0x06, 0x6e, 0xa3,
+ 0x75, 0x37, 0x34, 0x66, 0xae, 0xc4, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73,
+ 0x46, 0x68, 0xb0, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, 0x46, 0x68, 0xb0,
+ 0x5c, 0x76, 0xea, 0x37, 0x53, 0x72, 0x28, 0xcd, 0x16, 0x0b, 0x8e, 0xdd,
+ 0x46, 0xea, 0x6e, 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0x6e,
+ 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0xc4, 0xf1, 0x57, 0x8c,
+ 0x34, 0xaf, 0x06, 0x69, 0xff, 0x00, 0x6b, 0xd5, 0x2e, 0x44, 0x28, 0xc7,
+ 0x08, 0x8a, 0x37, 0x3c, 0x87, 0xd1, 0x47, 0x7f, 0xe5, 0x5c, 0x0d, 0x9f,
+ 0xed, 0x1f, 0xe1, 0xcb, 0x8b, 0xc1, 0x14, 0xd6, 0x97, 0xf6, 0xb0, 0x93,
+ 0x81, 0x3b, 0xa2, 0xb0, 0x1e, 0xe4, 0x06, 0x27, 0xf2, 0xcd, 0x78, 0xf8,
+ 0xac, 0xdf, 0x2f, 0xc0, 0xd4, 0x54, 0x71, 0x35, 0xa3, 0x19, 0x3e, 0x8d,
+ 0xfe, 0x7d, 0xbe, 0x64, 0xb9, 0x25, 0xb9, 0xeb, 0x05, 0xf6, 0x82, 0x4f,
+ 0x41, 0x5f, 0x10, 0xeb, 0x7a, 0x93, 0xeb, 0x3a, 0xcd, 0xf5, 0xfc, 0x84,
+ 0x97, 0xb9, 0x9d, 0xe6, 0x39, 0xff, 0x00, 0x69, 0x89, 0xfe, 0xb5, 0xf6,
+ 0xad, 0x8e, 0xa1, 0x6b, 0xab, 0x58, 0xc5, 0x75, 0x69, 0x34, 0x77, 0x36,
+ 0xb3, 0x2e, 0xe4, 0x91, 0x0e, 0x55, 0x85, 0x78, 0x5f, 0xfc, 0x2d, 0x5f,
+ 0x87, 0x3f, 0xf4, 0x25, 0x27, 0xfe, 0x00, 0xdb, 0xff, 0x00, 0x8d, 0x7c,
+ 0x67, 0x18, 0xe1, 0xa8, 0x63, 0x61, 0x87, 0x55, 0x31, 0x31, 0xa7, 0x1f,
+ 0x79, 0xab, 0xdd, 0xf3, 0x7c, 0x3a, 0xab, 0x76, 0xfd, 0x4c, 0xea, 0x59,
+ 0xdb, 0x53, 0xc4, 0x68, 0xaf, 0x6e, 0xff, 0x00, 0x85, 0xab, 0xf0, 0xe7,
+ 0xfe, 0x84, 0xa4, 0xff, 0x00, 0xc0, 0x1b, 0x7f, 0xf1, 0xa3, 0xfe, 0x16,
+ 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
+ 0xc6, 0xbf, 0x33, 0xfe, 0xc5, 0xc0, 0xff, 0x00, 0xd0, 0x7c, 0x3e, 0xe9,
+ 0x7f, 0x91, 0x8f, 0x2a, 0xee, 0x78, 0x8d, 0x15, 0xed, 0xdf, 0xf0, 0xb5,
+ 0x7e, 0x1c, 0xff, 0x00, 0xd0, 0x94, 0x9f, 0xf8, 0x03, 0x6f, 0xfe, 0x34,
+ 0x7f, 0xc2, 0xd5, 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d,
+ 0xbf, 0xf8, 0xd1, 0xfd, 0x8b, 0x81, 0xff, 0x00, 0xa0, 0xf8, 0x7d, 0xd2,
+ 0xff, 0x00, 0x20, 0xe5, 0x5d, 0xcf, 0x11, 0xa2, 0xbd, 0xbb, 0xfe, 0x16,
+ 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00,
+ 0xc6, 0x8f, 0xf8, 0x5a, 0xbf, 0x0e, 0x7f, 0xe8, 0x4a, 0x4f, 0xfc, 0x01,
+ 0xb7, 0xff, 0x00, 0x1a, 0x3f, 0xb1, 0x70, 0x3f, 0xf4, 0x1f, 0x0f, 0xba,
+ 0x5f, 0xe4, 0x1c, 0xab, 0xb9, 0xe2, 0x34, 0x57, 0xb7, 0x7f, 0xc2, 0xd5,
+ 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd1,
+ 0xff, 0x00, 0x0b, 0x57, 0xe1, 0xcf, 0xfd, 0x09, 0x49, 0xff, 0x00, 0x80,
+ 0x36, 0xff, 0x00, 0xe3, 0x47, 0xf6, 0x2e, 0x07, 0xfe, 0x83, 0xe1, 0xf7,
+ 0x4b, 0xfc, 0x83, 0x95, 0x77, 0x3c, 0x52, 0x09, 0xde, 0xda, 0x78, 0xe6,
+ 0x89, 0x8a, 0x49, 0x1b, 0x07, 0x56, 0x1d, 0x41, 0x07, 0x20, 0xd7, 0xdb,
+ 0xfa, 0x5d, 0xf0, 0xd4, 0x74, 0xdb, 0x4b, 0xb0, 0x30, 0x27, 0x85, 0x25,
+ 0x03, 0xfd, 0xe5, 0x07, 0xfa, 0xd7, 0x85, 0xff, 0x00, 0xc2, 0xd5, 0xf8,
+ 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd7, 0xb6,
+ 0x69, 0x1a, 0x95, 0xa5, 0xc7, 0x87, 0xec, 0xaf, 0xe1, 0x55, 0xb3, 0xb1,
+ 0x7b, 0x54, 0x99, 0x11, 0xf0, 0x82, 0x28, 0xca, 0x02, 0x01, 0xec, 0x30,
+ 0x3f, 0x0e, 0x2b, 0xf4, 0x7e, 0x0e, 0xc2, 0xd0, 0xc1, 0xce, 0xbc, 0x69,
+ 0x62, 0x63, 0x51, 0x34, 0x9b, 0x4a, 0xfa, 0x5a, 0xfa, 0xeb, 0xea, 0x6d,
+ 0x4e, 0xca, 0xfa, 0x9a, 0x5b, 0xa8, 0xdd, 0x5e, 0x57, 0xab, 0xfe, 0xd1,
+ 0x3e, 0x1b, 0xd3, 0xaf, 0x1a, 0x0b, 0x68, 0x6e, 0xf5, 0x15, 0x53, 0x83,
+ 0x34, 0x28, 0xaa, 0x87, 0xe9, 0xb8, 0x82, 0x7f, 0x2a, 0xeb, 0xbc, 0x1b,
+ 0xf1, 0x07, 0x45, 0xf1, 0xcd, 0xbb, 0xbe, 0x99, 0x70, 0x7c, 0xe8, 0xc6,
+ 0x64, 0xb6, 0x98, 0x6d, 0x91, 0x07, 0xa9, 0x1d, 0xc7, 0xb8, 0xc8, 0xaf,
+ 0xb7, 0xc3, 0xe7, 0x19, 0x76, 0x2a, 0xb7, 0xd5, 0xe8, 0x56, 0x8c, 0xa7,
+ 0xd9, 0x3f, 0xcb, 0xbf, 0xc8, 0xd1, 0x49, 0x3d, 0x2e, 0x74, 0xdb, 0xa8,
+ 0xcd, 0x37, 0x34, 0x64, 0x57, 0xb2, 0x55, 0xc7, 0x6e, 0xa3, 0x75, 0x37,
+ 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b,
+ 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e,
+ 0xa3, 0x75, 0x37, 0x34, 0x64, 0x51, 0x60, 0xb8, 0xed, 0xd4, 0x6e, 0xa6,
+ 0xe4, 0x51, 0x9a, 0x2c, 0x17, 0x1b, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75,
+ 0x55, 0x88, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, 0xd4, 0x58, 0x2e,
+ 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, 0xc9, 0x32, 0x28,
+ 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, 0x1e, 0xea,
+ 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8c, 0xd3, 0xb0,
+ 0x5c, 0xf9, 0x7b, 0xe3, 0xb6, 0xaf, 0x3e, 0xa5, 0xf1, 0x0e, 0xf2, 0xde,
+ 0x47, 0x26, 0x1b, 0x24, 0x48, 0x62, 0x4e, 0xc0, 0x15, 0x0c, 0x4f, 0xe2,
+ 0x58, 0xfe, 0x95, 0xe7, 0x95, 0xda, 0x7c, 0x64, 0xff, 0x00, 0x92, 0x97,
+ 0xad, 0xff, 0x00, 0xbf, 0x1f, 0xfe, 0x8a, 0x4a, 0xe2, 0xeb, 0xf9, 0x47,
+ 0x3a, 0x9c, 0xaa, 0x66, 0x78, 0x99, 0x49, 0xdd, 0xf3, 0xcb, 0xf0, 0x6d,
+ 0x23, 0x92, 0x5b, 0x9e, 0xf7, 0xfb, 0x34, 0xeb, 0x13, 0xcd, 0x67, 0xac,
+ 0xe9, 0xb2, 0x39, 0x6b, 0x78, 0x1a, 0x39, 0xa2, 0x04, 0xfd, 0xd2, 0xdb,
+ 0x83, 0x0f, 0xc7, 0x68, 0xfd, 0x6b, 0xc1, 0x2b, 0xda, 0xff, 0x00, 0x66,
+ 0x73, 0x8b, 0xed, 0x7f, 0xfe, 0xb9, 0xc3, 0xfc, 0xde, 0xbc, 0x52, 0xbd,
+ 0x8c, 0xd2, 0x72, 0x9e, 0x4b, 0x97, 0x39, 0x3b, 0xdb, 0xda, 0xaf, 0x92,
+ 0x92, 0xb1, 0x4f, 0xe1, 0x41, 0x45, 0x14, 0x57, 0xc6, 0x90, 0x14, 0x51,
+ 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0xef,
+ 0x7f, 0x13, 0xf5, 0x89, 0xf4, 0xef, 0x82, 0xbe, 0x19, 0xb6, 0x81, 0xca,
+ 0x0b, 0xc8, 0x2d, 0x62, 0x94, 0x83, 0xd5, 0x04, 0x3b, 0x88, 0xfc, 0x48,
+ 0x15, 0xe0, 0x95, 0xed, 0x7f, 0x17, 0x0f, 0xfc, 0x5a, 0x5f, 0x05, 0xff,
+ 0x00, 0xd7, 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x96, 0x47, 0x39, 0x43, 0x03,
+ 0x98, 0x4a, 0x2e, 0xcf, 0x91, 0x7e, 0x32, 0xb3, 0xfc, 0x0a, 0x8e, 0xcc,
+ 0xf1, 0x4a, 0xe9, 0xbe, 0x1a, 0x6a, 0xf3, 0xe8, 0xbe, 0x3a, 0xd1, 0x67,
+ 0x81, 0xca, 0x99, 0x2e, 0x52, 0x07, 0x00, 0xfd, 0xe4, 0x76, 0x0a, 0xc0,
+ 0xfe, 0x07, 0xf4, 0xae, 0x66, 0xb6, 0x3c, 0x1b, 0xff, 0x00, 0x23, 0x7e,
+ 0x87, 0xff, 0x00, 0x5f, 0xd0, 0x7f, 0xe8, 0xc5, 0xaf, 0x9b, 0xc0, 0xce,
+ 0x54, 0xf1, 0x54, 0xa7, 0x07, 0x66, 0xa4, 0xbf, 0x31, 0x2d, 0xcf, 0xb4,
+ 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xaf, 0xeb, 0x9b, 0x1d, 0x57, 0x24,
+ 0xcd, 0x19, 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35,
+ 0x1e, 0xea, 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d,
+ 0xd4, 0x58, 0x2e, 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15,
+ 0xc9, 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xa2, 0xc3, 0xb8, 0xdd, 0xd4,
+ 0x9b, 0xa9, 0xbb, 0xa8, 0xdd, 0x5a, 0x19, 0xdc, 0x7e, 0xea, 0x4d, 0xd4,
+ 0xdd, 0xd4, 0x6e, 0xa0, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e,
+ 0xa0, 0x77, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, 0xa4, 0x2b, 0x8f,
+ 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0xc2, 0xe3, 0xb7, 0x52, 0xee,
+ 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb9, 0xf2, 0x9f, 0xc6, 0x3f, 0xf9, 0x29,
+ 0x5a, 0xdf, 0xfb, 0xf1, 0xff, 0x00, 0xe8, 0xb4, 0xae, 0x32, 0xbb, 0x2f,
+ 0x8c, 0x5c, 0xfc, 0x49, 0xd6, 0xff, 0x00, 0xdf, 0x8f, 0xff, 0x00, 0x45,
+ 0xa5, 0x71, 0xb5, 0xfc, 0x99, 0x9c, 0x7f, 0xc8, 0xcb, 0x13, 0xfe, 0x39,
+ 0xff, 0x00, 0xe9, 0x4c, 0xc1, 0xee, 0x7b, 0x57, 0xec, 0xd2, 0x71, 0x7d,
+ 0xaf, 0x7f, 0xd7, 0x38, 0x7f, 0x9b, 0xd7, 0x8a, 0xd7, 0xb4, 0x7e, 0xcd,
+ 0x67, 0x17, 0xda, 0xf7, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, 0xbd, 0x7b,
+ 0x19, 0x97, 0xfc, 0x89, 0x32, 0xef, 0xfb, 0x8b, 0xff, 0x00, 0xa5, 0x21,
+ 0xbd, 0x90, 0x51, 0x45, 0x15, 0xf2, 0x02, 0x0a, 0x28, 0xa2, 0x80, 0x0a,
+ 0x28, 0xa2, 0x80, 0x0a, 0x28, 0xa2, 0x80, 0x0a, 0xf6, 0xaf, 0x8b, 0x47,
+ 0x3f, 0x09, 0x7c, 0x17, 0xff, 0x00, 0x5c, 0xe0, 0xff, 0x00, 0xd1, 0x15,
+ 0xe2, 0xb5, 0xed, 0x1f, 0x16, 0x4f, 0xfc, 0x5a, 0x6f, 0x06, 0x7f, 0xd7,
+ 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x7e, 0x4d, 0xff, 0x00, 0x22, 0xfc, 0xc3,
+ 0xfc, 0x11, 0xff, 0x00, 0xd2, 0x90, 0xd6, 0xcc, 0xf1, 0x7a, 0xd8, 0xf0,
+ 0x67, 0xfc, 0x8e, 0x1a, 0x17, 0xfd, 0x7f, 0xc1, 0xff, 0x00, 0xa3, 0x16,
+ 0xb1, 0xeb, 0x63, 0xc1, 0xdf, 0xf2, 0x37, 0x68, 0x7f, 0xf5, 0xfd, 0x07,
+ 0xfe, 0x8c, 0x5a, 0xf9, 0xbc, 0x27, 0xfb, 0xcd, 0x3f, 0xf1, 0x2f, 0xcc,
+ 0x48, 0xfb, 0x2b, 0x75, 0x1b, 0xa9, 0x9b, 0xa8, 0xdd, 0x5f, 0xd8, 0x16,
+ 0x37, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb8, 0xed,
+ 0xd4, 0xbb, 0xa9, 0x9b, 0xa8, 0xdd, 0x4c, 0x2e, 0x3f, 0x75, 0x26, 0xea,
+ 0x6e, 0xea, 0x37, 0x52, 0x0b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d,
+ 0xd4, 0xec, 0x3b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0x0a,
+ 0xe4, 0x74, 0x53, 0x33, 0x4b, 0x93, 0x57, 0x63, 0x3b, 0x8e, 0xa2, 0x9b,
+ 0x9a, 0x4c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa3, 0x34, 0x58, 0x2e,
+ 0x3f, 0x34, 0x53, 0x33, 0x4b, 0x93, 0x45, 0x82, 0xe3, 0xa8, 0xcd, 0x33,
+ 0x34, 0x66, 0x8b, 0x05, 0xc7, 0xd1, 0x4d, 0xc9, 0xa3, 0x26, 0x8b, 0x05,
+ 0xcf, 0x96, 0x3e, 0x30, 0x7f, 0xc9, 0x48, 0xd6, 0xbf, 0xdf, 0x8f, 0xff,
+ 0x00, 0x45, 0xa5, 0x71, 0xb5, 0xdd, 0xfc, 0x6c, 0xd3, 0xa6, 0xb1, 0xf8,
+ 0x85, 0x7f, 0x2c, 0x8a, 0x44, 0x77, 0x4b, 0x1c, 0xd1, 0xb7, 0x66, 0x1b,
+ 0x02, 0x9f, 0xd5, 0x4d, 0x70, 0x95, 0xfc, 0x97, 0x9d, 0x42, 0x50, 0xcc,
+ 0xf1, 0x31, 0x92, 0xb7, 0xbf, 0x2f, 0xfd, 0x29, 0x90, 0x7b, 0x3f, 0xec,
+ 0xdb, 0xff, 0x00, 0x1f, 0xba, 0xef, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78,
+ 0xc5, 0x7b, 0x87, 0xec, 0xe3, 0xa7, 0x4d, 0x1c, 0x3a, 0xd5, 0xf3, 0x29,
+ 0x58, 0x24, 0x31, 0xc2, 0x8d, 0xfd, 0xe2, 0x37, 0x16, 0xfc, 0xb2, 0x3f,
+ 0x3a, 0xf0, 0xfa, 0xf6, 0x73, 0x58, 0x4a, 0x19, 0x26, 0x5b, 0xcc, 0xad,
+ 0x7f, 0x6a, 0xff, 0x00, 0xf2, 0x64, 0x01, 0x45, 0x14, 0x57, 0xc6, 0x00,
+ 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00,
+ 0x57, 0xb3, 0xfc, 0x57, 0xff, 0x00, 0x92, 0x51, 0xe0, 0xdf, 0xfa, 0xe7,
+ 0x07, 0xfe, 0x88, 0xaf, 0x18, 0xaf, 0x70, 0xf8, 0x9b, 0xa7, 0x4d, 0x79,
+ 0xf0, 0x77, 0xc3, 0x37, 0x11, 0x29, 0x74, 0xb5, 0x8a, 0xd9, 0xe4, 0xc7,
+ 0x65, 0x30, 0xed, 0xcf, 0xe6, 0x40, 0xfc, 0x6b, 0xec, 0xf2, 0x38, 0x4a,
+ 0x78, 0x0c, 0xc1, 0x45, 0x5f, 0xdc, 0x5f, 0x84, 0xae, 0xc0, 0xf0, 0xfa,
+ 0xd8, 0xf0, 0x6f, 0xfc, 0x8d, 0xfa, 0x1f, 0xfd, 0x7f, 0x41, 0xff, 0x00,
+ 0xa3, 0x16, 0xb1, 0xeb, 0xa2, 0xf8, 0x79, 0xa7, 0x4d, 0xaa, 0x78, 0xdf,
+ 0x45, 0x86, 0x15, 0x2c, 0xcb, 0x75, 0x1c, 0xad, 0x8e, 0xca, 0x8c, 0x18,
+ 0x9f, 0xc8, 0x57, 0xcd, 0x60, 0x61, 0x29, 0xe2, 0xe9, 0x46, 0x2a, 0xed,
+ 0xca, 0x3f, 0x9a, 0x03, 0xeb, 0x9c, 0xd1, 0x4c, 0xcd, 0x2e, 0x4d, 0x7f,
+ 0x60, 0x58, 0xbb, 0x8e, 0xa2, 0x99, 0x9a, 0x33, 0x45, 0x82, 0xe3, 0xe8,
+ 0xa6, 0x66, 0x8c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa5, 0xc9, 0xa2,
+ 0xc1, 0x71, 0xd9, 0xa2, 0x9b, 0x93, 0x49, 0x9a, 0x2c, 0x17, 0x1f, 0x45,
+ 0x33, 0x34, 0x66, 0x8b, 0x05, 0xc6, 0x6e, 0xa3, 0x75, 0x30, 0x90, 0x3a,
+ 0x9c, 0x51, 0x9a, 0xd2, 0xc6, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xda, 0x4c,
+ 0xd1, 0x60, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x02, 0x0f, 0x43, 0x9a, 0x37,
+ 0x01, 0xdf, 0x14, 0x58, 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x94, 0x58,
+ 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x80, 0x83, 0xd0, 0xe6, 0x8b, 0x05,
+ 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x3a, 0x9c, 0x51, 0x9a, 0x2c, 0x17,
+ 0x31, 0x3c, 0x5d, 0xe0, 0xcd, 0x2f, 0xc6, 0xb6, 0x2b, 0x6f, 0xa8, 0xc4,
+ 0x4b, 0x26, 0x4c, 0x53, 0xc6, 0x71, 0x24, 0x64, 0xf5, 0xc1, 0xfe, 0x87,
+ 0x8a, 0xe0, 0x6d, 0x3f, 0x67, 0x7d, 0x32, 0x2b, 0xb0, 0xf7, 0x1a, 0xad,
+ 0xcc, 0xf6, 0xe0, 0xe7, 0xca, 0x58, 0xd5, 0x09, 0x1e, 0x85, 0xb2, 0x7f,
+ 0x95, 0x7a, 0xd5, 0x25, 0x78, 0x38, 0xcc, 0x87, 0x2c, 0xcc, 0x2b, 0x2a,
+ 0xf8, 0x9a, 0x2a, 0x52, 0xef, 0xaa, 0xfb, 0xec, 0xd5, 0xfe, 0x77, 0x0b,
+ 0x95, 0xf4, 0xad, 0x32, 0xd3, 0x44, 0xb0, 0x86, 0xca, 0xc6, 0x05, 0xb7,
+ 0xb6, 0x88, 0x61, 0x23, 0x4e, 0x83, 0xfc, 0x4f, 0xbd, 0x7c, 0x6b, 0x5f,
+ 0x69, 0x57, 0xc5, 0xb5, 0xf9, 0x8f, 0x88, 0xb0, 0x8d, 0x38, 0xe0, 0xe1,
+ 0x05, 0x64, 0xb9, 0xec, 0x97, 0xfd, 0xb8, 0x34, 0x14, 0x51, 0x45, 0x7e,
+ 0x32, 0x30, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a,
+ 0x28, 0x00, 0xaf, 0xad, 0xfc, 0x2b, 0x04, 0x57, 0x9e, 0x06, 0xd1, 0xad,
+ 0xe7, 0x8d, 0x66, 0x86, 0x4d, 0x3a, 0x04, 0x78, 0xdc, 0x65, 0x58, 0x18,
+ 0xd7, 0x20, 0x8a, 0xf9, 0x22, 0xbe, 0xba, 0xf0, 0x67, 0xfc, 0x89, 0xfa,
+ 0x17, 0xfd, 0x78, 0x41, 0xff, 0x00, 0xa2, 0xd6, 0xbf, 0x5c, 0xf0, 0xee,
+ 0x2a, 0x58, 0x9c, 0x42, 0x7b, 0x72, 0xaf, 0xcc, 0x47, 0x05, 0xab, 0x7e,
+ 0xcf, 0x7a, 0x4d, 0xdd, 0xdb, 0x4b, 0x65, 0xa8, 0x4f, 0x63, 0x13, 0x1c,
+ 0xf9, 0x25, 0x04, 0x80, 0x7b, 0x02, 0x48, 0x3f, 0x9e, 0x6b, 0xaf, 0xf0,
+ 0x57, 0xc3, 0xcd, 0x27, 0xc0, 0xd1, 0xb9, 0xb3, 0x47, 0x9a, 0xee, 0x41,
+ 0xb6, 0x4b, 0xa9, 0xb0, 0x5c, 0x8f, 0x41, 0xe8, 0x3d, 0x87, 0xe3, 0x5d,
+ 0x36, 0x69, 0x6b, 0xf5, 0x7c, 0x37, 0x0f, 0xe5, 0x78, 0x3a, 0xff, 0x00,
+ 0x59, 0xa1, 0x41, 0x46, 0x7d, 0xf5, 0xd3, 0xd1, 0x6c, 0xbe, 0x49, 0x0a,
+ 0xe3, 0xb7, 0x51, 0xba, 0x99, 0x9a, 0x03, 0x03, 0xde, 0xbd, 0xfb, 0x05,
+ 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x06, 0x4f, 0x14, 0x51, 0x60, 0xb8,
+ 0xfd, 0xd4, 0x6e, 0xa6, 0xd1, 0x45, 0x82, 0xe3, 0xb7, 0x51, 0xba, 0x98,
+ 0x08, 0x3d, 0x0e, 0x68, 0x24, 0x0e, 0xf4, 0x58, 0x2e, 0x3f, 0x75, 0x1b,
+ 0xa9, 0x94, 0xb4, 0x58, 0x2e, 0x3b, 0x75, 0x1b, 0xa9, 0x99, 0xa0, 0x10,
+ 0x7a, 0x1a, 0x2c, 0x17, 0x31, 0x62, 0xbd, 0x91, 0x46, 0xf7, 0x90, 0x3e,
+ 0xd3, 0xf7, 0x1b, 0xa9, 0xad, 0x58, 0xa5, 0x13, 0x46, 0xae, 0xbd, 0x08,
+ 0xac, 0x3f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, 0x91, 0x56, 0x52, 0xf1,
+ 0xe3, 0x48, 0x55, 0x42, 0x80, 0x7a, 0x80, 0x3d, 0xeb, 0xb2, 0x70, 0xbe,
+ 0xc7, 0x9d, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x36, 0x59, 0x44, 0x31,
+ 0xb3, 0xb7, 0x41, 0x49, 0xba, 0xb3, 0x1e, 0xf2, 0x49, 0x12, 0x65, 0x60,
+ 0x08, 0x1d, 0x01, 0x1e, 0xf5, 0x84, 0x61, 0xcc, 0x75, 0x4e, 0xa7, 0x22,
+ 0x1b, 0x2d, 0xec, 0x8c, 0x0b, 0xa4, 0x81, 0x37, 0x1f, 0xb8, 0xbd, 0x7e,
+ 0xb4, 0xb1, 0x5e, 0xc8, 0xa0, 0x3b, 0xb8, 0x7d, 0xa7, 0xee, 0x37, 0x5f,
+ 0xad, 0x57, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x14, 0x79, 0xc7, 0xfb,
+ 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xeb, 0xe5, 0x56, 0xb5, 0x8e, 0x0e, 0x77,
+ 0x7b, 0xdc, 0xdd, 0x8e, 0x41, 0x2a, 0x2b, 0xaf, 0x42, 0x33, 0x4e, 0xac,
+ 0xa8, 0xef, 0x1d, 0x3c, 0x85, 0x00, 0x05, 0x3d, 0x40, 0x1e, 0xe6, 0xb4,
+ 0x77, 0x57, 0x24, 0xa3, 0xca, 0x77, 0xc2, 0x7c, 0xe8, 0x74, 0x92, 0x08,
+ 0xd1, 0x99, 0x8f, 0x00, 0x66, 0xb2, 0xa5, 0xbd, 0x91, 0x81, 0x74, 0x70,
+ 0x99, 0x38, 0xd8, 0x3a, 0xfd, 0x69, 0xd2, 0x5e, 0x3b, 0xf9, 0xea, 0x40,
+ 0x2a, 0x3a, 0x02, 0x3d, 0xc5, 0x55, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9,
+ 0x15, 0xbc, 0x21, 0x6d, 0xce, 0x6a, 0x95, 0x6f, 0xa2, 0x64, 0xf1, 0x5e,
+ 0x48, 0xa3, 0x7b, 0x48, 0x1f, 0x69, 0xfb, 0x8d, 0xd4, 0xd6, 0xac, 0x52,
+ 0x89, 0xa3, 0x57, 0x5e, 0x86, 0xb0, 0xfc, 0xe3, 0xfd, 0xc4, 0xff, 0x00,
+ 0xbe, 0x45, 0x59, 0x4b, 0xc7, 0x8d, 0x61, 0x0a, 0x14, 0x03, 0xd4, 0x01,
+ 0xef, 0x44, 0xe1, 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x15,
+ 0x1e, 0xea, 0x5d, 0xd5, 0xcd, 0x63, 0xba, 0xe3, 0xeb, 0xc0, 0xbc, 0x71,
+ 0xf0, 0x57, 0x55, 0x83, 0x57, 0x9e, 0xe7, 0x44, 0x85, 0x6f, 0x6c, 0xa6,
+ 0x72, 0xe2, 0x25, 0x70, 0xaf, 0x16, 0x4e, 0x76, 0xe0, 0x91, 0x91, 0xe9,
+ 0x8a, 0xf7, 0xad, 0xd4, 0x66, 0xbe, 0x7f, 0x39, 0xc8, 0xf0, 0x99, 0xe5,
+ 0x18, 0xd2, 0xc4, 0xdd, 0x72, 0xbb, 0xa6, 0xb7, 0x5d, 0xfb, 0xef, 0xe8,
+ 0x17, 0xb1, 0xf2, 0xc5, 0xff, 0x00, 0xc3, 0x5f, 0x12, 0xe9, 0x96, 0x53,
+ 0x5d, 0xdd, 0x69, 0x52, 0x43, 0x6f, 0x0a, 0x97, 0x92, 0x42, 0xe8, 0x42,
+ 0x81, 0xd4, 0xf0, 0x6b, 0x99, 0xaf, 0xaa, 0xfe, 0x21, 0x9c, 0xf8, 0x1f,
+ 0x5c, 0xff, 0x00, 0xaf, 0x47, 0xfe, 0x55, 0xf2, 0xa5, 0x7e, 0x01, 0xc5,
+ 0x59, 0x1e, 0x1f, 0x22, 0xc4, 0x53, 0xa3, 0x87, 0x93, 0x92, 0x94, 0x6f,
+ 0xef, 0x5b, 0xbd, 0xba, 0x24, 0x5a, 0x77, 0x0a, 0xe9, 0xec, 0xbe, 0x19,
+ 0xf8, 0x9b, 0x51, 0xb3, 0x86, 0xea, 0xdb, 0x49, 0x92, 0x5b, 0x79, 0x90,
+ 0x49, 0x1b, 0x87, 0x40, 0x19, 0x48, 0xc8, 0x3d, 0x6b, 0x98, 0xaf, 0xac,
+ 0x3c, 0x08, 0x71, 0xe0, 0xad, 0x0b, 0xfe, 0xbc, 0xa1, 0xff, 0x00, 0xd0,
+ 0x05, 0x57, 0x0a, 0xe4, 0x58, 0x7c, 0xf6, 0xbd, 0x5a, 0x58, 0x89, 0x4a,
+ 0x2a, 0x2a, 0xfe, 0xed, 0xbb, 0xdb, 0xaa, 0x60, 0xdd, 0x8f, 0x9e, 0xdb,
+ 0xe1, 0x4f, 0x8a, 0xd1, 0x4b, 0x1d, 0x1a, 0x50, 0x00, 0xc9, 0x3e, 0x62,
+ 0x7f, 0xf1, 0x55, 0xc9, 0xd7, 0xd9, 0x17, 0x4d, 0xfe, 0x8d, 0x37, 0xfb,
+ 0x87, 0xf9, 0x57, 0xc6, 0xf5, 0xd1, 0xc5, 0x9c, 0x3d, 0x86, 0xc8, 0x5d,
+ 0x05, 0x87, 0x9c, 0xa5, 0xcf, 0xcd, 0x7e, 0x6b, 0x74, 0xb6, 0xd6, 0x4b,
+ 0xb8, 0x27, 0x70, 0xae, 0x87, 0x47, 0xf0, 0x07, 0x88, 0x35, 0xfb, 0x04,
+ 0xbd, 0xb0, 0xd3, 0x64, 0xb9, 0xb5, 0x72, 0x42, 0xc8, 0xae, 0xa0, 0x12,
+ 0x0e, 0x0f, 0x53, 0xeb, 0x5c, 0xf5, 0x7d, 0x27, 0xf0, 0x54, 0xe3, 0xe1,
+ 0xed, 0x8f, 0xfd, 0x74, 0x97, 0xff, 0x00, 0x43, 0x35, 0xe7, 0x70, 0xbe,
+ 0x4f, 0x43, 0x3c, 0xc6, 0xcb, 0x0d, 0x5e, 0x4d, 0x25, 0x16, 0xf4, 0xb5,
+ 0xee, 0x9a, 0x5d, 0x53, 0xee, 0x0d, 0xd8, 0xf3, 0x1f, 0x0d, 0x7c, 0x12,
+ 0xd7, 0xb5, 0x3d, 0x42, 0x31, 0xa9, 0x40, 0x34, 0xdb, 0x20, 0xc0, 0xc8,
+ 0xee, 0xea, 0xce, 0x47, 0x70, 0xa0, 0x13, 0xcf, 0xb9, 0xe2, 0xbe, 0x87,
+ 0xb7, 0x82, 0x3b, 0x4b, 0x78, 0xa0, 0x89, 0x42, 0x45, 0x12, 0x84, 0x45,
+ 0x1d, 0x80, 0x18, 0x02, 0x97, 0x34, 0x9b, 0xab, 0xfa, 0x07, 0x25, 0xe1,
+ 0xfc, 0x1e, 0x45, 0x09, 0x47, 0x0d, 0x76, 0xe5, 0xbb, 0x7a, 0xbd, 0x36,
+ 0x5a, 0x24, 0xad, 0xf2, 0x22, 0xf7, 0x24, 0xcd, 0x19, 0xa8, 0xf7, 0x51,
+ 0xba, 0xbe, 0x96, 0xc1, 0x71, 0xd2, 0x48, 0x22, 0x46, 0x76, 0xe0, 0x0e,
+ 0x6b, 0x2a, 0x5b, 0xd9, 0x18, 0x17, 0x47, 0x11, 0xe4, 0xe3, 0x60, 0xeb,
+ 0xf5, 0xa7, 0x49, 0x78, 0xef, 0xe7, 0xa9, 0x00, 0xa8, 0xe8, 0x08, 0xf7,
+ 0x02, 0xaa, 0xf9, 0xc7, 0xfb, 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xe9, 0x84,
+ 0x2d, 0xb9, 0xc5, 0x56, 0xaf, 0x36, 0x89, 0x93, 0xc5, 0x7b, 0x22, 0x8d,
+ 0xef, 0x20, 0x7d, 0xa7, 0xee, 0x37, 0x53, 0x5a, 0xd1, 0x4a, 0x26, 0x8d,
+ 0x5d, 0x7a, 0x1a, 0xc2, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x15, 0x65,
+ 0x2f, 0x1e, 0x34, 0x84, 0x28, 0x50, 0x09, 0xe4, 0x01, 0xef, 0x44, 0xe1,
+ 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xa6, 0xcb, 0x28, 0x86, 0x36,
+ 0x76, 0xe8, 0x29, 0x37, 0x56, 0x63, 0xde, 0x3c, 0x89, 0x30, 0x60, 0xa4,
+ 0x0e, 0x80, 0x8f, 0x7a, 0xc2, 0x30, 0xe6, 0x3a, 0xa7, 0x53, 0x91, 0x0d,
+ 0x96, 0xf6, 0x46, 0x05, 0xd2, 0x40, 0x9b, 0x8f, 0xdc, 0x5e, 0xa3, 0xde,
+ 0x96, 0x2b, 0xd9, 0x14, 0x07, 0x77, 0x0f, 0xb4, 0xfd, 0xc6, 0xeb, 0xf5,
+ 0xaa, 0xfe, 0x71, 0xfe, 0xe2, 0x7f, 0xdf, 0x22, 0x8f, 0x38, 0xff, 0x00,
+ 0x71, 0x3f, 0xef, 0x91, 0x5d, 0x7c, 0xaa, 0xd6, 0xb1, 0xc1, 0xce, 0xef,
+ 0x7b, 0x9b, 0xb1, 0xc8, 0x25, 0x45, 0x75, 0xe8, 0x46, 0x69, 0xd5, 0x95,
+ 0x1d, 0xe3, 0xa0, 0x81, 0x46, 0x02, 0x9e, 0xa0, 0x0f, 0x72, 0x2b, 0x4b,
+ 0x35, 0xc9, 0x28, 0xf2, 0x9d, 0xf0, 0xa9, 0xce, 0x85, 0x92, 0x41, 0x1a,
+ 0x33, 0xb7, 0x40, 0x32, 0x6b, 0x2a, 0x5b, 0xd9, 0x1c, 0x17, 0x47, 0x09,
+ 0x93, 0x8d, 0x83, 0xaf, 0xd6, 0x9d, 0x25, 0xe3, 0xbf, 0x9e, 0xa4, 0x02,
+ 0xa3, 0xa0, 0x23, 0xdc, 0x55, 0x5f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef,
+ 0x91, 0x5b, 0xc2, 0x16, 0xdc, 0xe6, 0xa9, 0x57, 0x9b, 0x44, 0xc8, 0xea,
+ 0x70, 0xa4, 0x88, 0x30, 0x09, 0xff, 0x00, 0xf5, 0xd3, 0x4d, 0xbb, 0x09,
+ 0x84, 0x59, 0x1b, 0xbd, 0x7b, 0x56, 0x95, 0xb4, 0x66, 0x18, 0x42, 0x13,
+ 0x92, 0x3d, 0x2a, 0xe5, 0x2b, 0x23, 0x2a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
+ 0xc8, 0x2a, 0x40, 0x9f, 0x20, 0x8f, 0xff, 0x00, 0x5d, 0x6a, 0xe6, 0xa3,
+ 0xb9, 0x8c, 0xcf, 0x09, 0x40, 0x40, 0x27, 0xd6, 0xb1, 0x83, 0xe5, 0x3a,
+ 0x6a, 0x47, 0x99, 0x5c, 0xc7, 0xa2, 0xa5, 0x5b, 0x76, 0x69, 0x8c, 0x59,
+ 0x1b, 0xbf, 0x4a, 0x3e, 0xce, 0xde, 0x7f, 0x95, 0x91, 0xbb, 0xd7, 0xb5,
+ 0x74, 0xdd, 0x1c, 0x3c, 0xac, 0x7a, 0xa9, 0x2d, 0x6f, 0x80, 0x7f, 0xcb,
+ 0x1a, 0xd6, 0xa8, 0x6d, 0xe3, 0x30, 0xc2, 0xa8, 0x48, 0x24, 0x77, 0x15,
+ 0x26, 0x6b, 0x9a, 0x6f, 0x99, 0x9d, 0xf4, 0xe3, 0xca, 0x8c, 0xb6, 0x52,
+ 0x1a, 0xe3, 0x20, 0xff, 0x00, 0x96, 0x15, 0x5e, 0xb6, 0x2e, 0x23, 0x33,
+ 0x44, 0xc8, 0x08, 0x04, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
+ 0x7a, 0xf6, 0xad, 0xa1, 0x24, 0xd1, 0xcb, 0x52, 0x0e, 0x2d, 0x58, 0x8a,
+ 0xa7, 0x0a, 0x48, 0x83, 0x00, 0x9f, 0xff, 0x00, 0x5d, 0x34, 0xdb, 0xb2,
+ 0xcc, 0x22, 0xc8, 0xdd, 0xeb, 0xda, 0xb4, 0xad, 0xa3, 0x30, 0xc2, 0xaa,
+ 0x4e, 0x48, 0xf4, 0xa2, 0x52, 0xb2, 0x0a, 0x70, 0x72, 0x6d, 0x32, 0x7a,
+ 0x29, 0xa4, 0xd1, 0x9a, 0xe5, 0xb1, 0xde, 0x3a, 0x8a, 0x6e, 0x73, 0x41,
+ 0x34, 0x58, 0x0e, 0x7f, 0xe2, 0x1f, 0xfc, 0x88, 0xfa, 0xe7, 0xfd, 0x7a,
+ 0xbf, 0xf2, 0xaf, 0x95, 0xab, 0xeb, 0x2f, 0x18, 0x58, 0x4b, 0xaa, 0xf8,
+ 0x57, 0x56, 0xb4, 0x80, 0x6e, 0x9a, 0x6b, 0x69, 0x15, 0x17, 0xd5, 0xb6,
+ 0x9c, 0x0f, 0xce, 0xbe, 0x4e, 0x65, 0x2a, 0x48, 0x20, 0x82, 0x38, 0x20,
+ 0xf6, 0xaf, 0xc1, 0x7c, 0x46, 0x84, 0x96, 0x32, 0x84, 0xed, 0xa3, 0x8b,
+ 0x5f, 0x73, 0xff, 0x00, 0x82, 0x8d, 0x20, 0x25, 0x7d, 0x5d, 0xe0, 0x5f,
+ 0xf9, 0x12, 0xf4, 0x2f, 0xfa, 0xf2, 0x87, 0xff, 0x00, 0x40, 0x15, 0xf2,
+ 0x9a, 0x23, 0x48, 0xea, 0x88, 0xa5, 0x9d, 0x8e, 0x02, 0x81, 0x92, 0x4d,
+ 0x7d, 0x69, 0xe1, 0x8b, 0x19, 0x34, 0xbf, 0x0d, 0xe9, 0x76, 0x73, 0x71,
+ 0x2c, 0x16, 0xd1, 0xc6, 0xe3, 0xd0, 0x85, 0x00, 0xd5, 0x78, 0x73, 0x09,
+ 0x3c, 0x56, 0x22, 0x76, 0xd1, 0x45, 0x2f, 0xc7, 0xfe, 0x00, 0x4c, 0xd0,
+ 0xba, 0xff, 0x00, 0x8f, 0x69, 0xbf, 0xdc, 0x3f, 0xca, 0xbe, 0x39, 0xaf,
+ 0xb1, 0xe5, 0x5f, 0x32, 0x27, 0x4c, 0xe3, 0x72, 0x91, 0x9a, 0xf9, 0x03,
+ 0x50, 0xb1, 0x9b, 0x4c, 0xbe, 0xb8, 0xb4, 0x9d, 0x0a, 0x4d, 0x04, 0x86,
+ 0x37, 0x53, 0xd8, 0x83, 0x8a, 0xed, 0xf1, 0x22, 0x12, 0xff, 0x00, 0x65,
+ 0x9d, 0xb4, 0xf7, 0xd7, 0xfe, 0x92, 0x10, 0x2b, 0xd7, 0xd2, 0x5f, 0x05,
+ 0xbf, 0xe4, 0x9f, 0x58, 0xff, 0x00, 0xd7, 0x49, 0x7f, 0xf4, 0x33, 0x5f,
+ 0x36, 0xd7, 0xd3, 0x9f, 0x0a, 0x74, 0xe9, 0xb4, 0xbf, 0x01, 0xe9, 0x91,
+ 0x4e, 0xa5, 0x24, 0x75, 0x69, 0x76, 0x9e, 0xa0, 0x33, 0x12, 0x3f, 0x42,
+ 0x2b, 0xc3, 0xf0, 0xf6, 0x12, 0x96, 0x69, 0x52, 0x49, 0x68, 0xa0, 0xff,
+ 0x00, 0x19, 0x44, 0x72, 0xd8, 0xeb, 0xe8, 0xa6, 0xe6, 0x8c, 0xd7, 0xf4,
+ 0x3d, 0x8c, 0x87, 0x51, 0x4d, 0xcd, 0x19, 0xa2, 0xc0, 0x65, 0xb2, 0x90,
+ 0xd7, 0x19, 0x04, 0x7f, 0xfb, 0x42, 0xab, 0xd6, 0xc5, 0xc4, 0x66, 0x68,
+ 0x59, 0x07, 0x04, 0xfa, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37,
+ 0x7a, 0xf6, 0xae, 0xa8, 0x4a, 0xe8, 0xe0, 0xa9, 0x07, 0x16, 0xac, 0x45,
+ 0x53, 0x85, 0x24, 0x41, 0x80, 0x4f, 0xff, 0x00, 0xae, 0x9a, 0x6d, 0xd9,
+ 0x66, 0x11, 0x64, 0x6e, 0xf5, 0xed, 0x5a, 0x76, 0xd1, 0x98, 0x21, 0x0a,
+ 0x48, 0x27, 0xda, 0x89, 0x4a, 0xc8, 0x29, 0xc1, 0xc9, 0xb4, 0xc9, 0xab,
+ 0x20, 0xa9, 0x02, 0x7c, 0x82, 0x3f, 0xfd, 0x75, 0xab, 0x9a, 0x8a, 0xe6,
+ 0x33, 0x34, 0x45, 0x41, 0xc1, 0xf7, 0xac, 0x60, 0xf9, 0x4e, 0x9a, 0x91,
+ 0xe6, 0x46, 0x45, 0x15, 0x28, 0xb7, 0x66, 0x98, 0xc5, 0x91, 0xb8, 0x77,
+ 0xed, 0x47, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, 0x7a, 0xf6, 0xae, 0x9b,
+ 0xa3, 0x87, 0x95, 0x8f, 0x55, 0x25, 0xad, 0xf0, 0x0f, 0xf9, 0x63, 0x5a,
+ 0xd5, 0x0d, 0xba, 0x18, 0x61, 0x54, 0x24, 0x12, 0x3d, 0x2a, 0x4c, 0xd7,
+ 0x34, 0xdf, 0x33, 0x3b, 0xe9, 0xc7, 0x95, 0x19, 0x6c, 0xa4, 0x35, 0xc6,
+ 0x41, 0xff, 0x00, 0x2c, 0x2a, 0xbd, 0x6c, 0x5c, 0x46, 0x66, 0x85, 0x90,
+ 0x1c, 0x13, 0xeb, 0x59, 0x9f, 0x67, 0x6f, 0x3f, 0xca, 0xc8, 0xdd, 0xfa,
+ 0x56, 0xd0, 0x92, 0x68, 0xe5, 0xa9, 0x06, 0x9a, 0xb1, 0xa0, 0xd6, 0xc1,
+ 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, 0x6a, 0x28, 0xae, 0x76, 0xdb, 0x3a,
+ 0xd2, 0xb6, 0xc1, 0x45, 0x14, 0x52, 0x19, 0x0a, 0xdb, 0x05, 0xb8, 0x32,
+ 0xe4, 0xe4, 0xf6, 0xa3, 0xec, 0xc3, 0xed, 0x1e, 0x6e, 0x4e, 0x7d, 0x2a,
+ 0x6a, 0x2a, 0xb9, 0x99, 0x3c, 0xa8, 0x28, 0xa2, 0x8a, 0x92, 0x82, 0xa1,
+ 0xfb, 0x30, 0xfb, 0x47, 0x9b, 0x93, 0x9f, 0x4a, 0x9a, 0x8a, 0x69, 0xd8,
+ 0x4d, 0x5f, 0x72, 0x16, 0xb6, 0x0d, 0x70, 0x25, 0xc9, 0xc8, 0xed, 0x53,
+ 0x51, 0x45, 0x0d, 0xb6, 0x09, 0x5b, 0x60, 0xa2, 0x8a, 0x29, 0x0c, 0x28,
+ 0xa2, 0x8a, 0x00, 0x2b, 0x83, 0xf1, 0x57, 0xc1, 0xdd, 0x1b, 0xc4, 0xb7,
+ 0xaf, 0x79, 0x1b, 0xcb, 0xa7, 0x5d, 0x48, 0x73, 0x21, 0x80, 0x02, 0x8e,
+ 0x7d, 0x4a, 0x9e, 0xff, 0x00, 0x42, 0x2b, 0xbc, 0xa2, 0xbc, 0xfc, 0x76,
+ 0x5f, 0x85, 0xcc, 0xa9, 0xfb, 0x1c, 0x5d, 0x35, 0x38, 0xf9, 0xfe, 0x8f,
+ 0x75, 0xf2, 0x1a, 0x6d, 0x1c, 0x3f, 0x84, 0xbe, 0x11, 0xe8, 0xde, 0x16,
+ 0xbb, 0x4b, 0xc2, 0xd2, 0x5f, 0xde, 0x27, 0x29, 0x24, 0xf8, 0xda, 0x87,
+ 0xd5, 0x54, 0x77, 0xf7, 0x39, 0xae, 0xe2, 0x8a, 0x29, 0xe0, 0xb0, 0x18,
+ 0x5c, 0xba, 0x97, 0xb1, 0xc2, 0x53, 0x50, 0x8f, 0x97, 0xeb, 0xd5, 0xfc,
+ 0xc2, 0xed, 0x85, 0x71, 0xfe, 0x31, 0xf8, 0x5f, 0xa4, 0x78, 0xc6, 0x6f,
+ 0xb4, 0xcd, 0xe6, 0x5a, 0x5e, 0xe3, 0x06, 0xe2, 0x0c, 0x65, 0xc7, 0x6d,
+ 0xc0, 0xf5, 0xfe, 0x7e, 0xf5, 0xd8, 0x51, 0x55, 0x8c, 0xc1, 0x61, 0xf1,
+ 0xf4, 0x9d, 0x0c, 0x54, 0x14, 0xe2, 0xfa, 0x3f, 0xeb, 0x46, 0x17, 0x68,
+ 0xf3, 0xbf, 0x0f, 0x7c, 0x11, 0xd1, 0x74, 0x6b, 0xc4, 0xb9, 0xb9, 0x96,
+ 0x5d, 0x49, 0xd0, 0xe5, 0x63, 0x94, 0x05, 0x8f, 0x3e, 0xa5, 0x47, 0x5f,
+ 0xc4, 0xe3, 0xda, 0xbd, 0x13, 0xa5, 0x14, 0x56, 0x38, 0x1c, 0xb7, 0x07,
+ 0x96, 0x41, 0xd3, 0xc1, 0xd3, 0x50, 0x4f, 0x7b, 0x75, 0xf5, 0x7b, 0xb0,
+ 0x6d, 0xb0, 0xa2, 0x8a, 0x2b, 0xd3, 0x10, 0x51, 0x45, 0x14, 0x00, 0x54,
+ 0x3f, 0x66, 0x1f, 0x68, 0xf3, 0x72, 0x73, 0xe9, 0x53, 0x51, 0x4d, 0x3b,
+ 0x09, 0xab, 0xee, 0x42, 0xd6, 0xc1, 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa,
+ 0x6a, 0x28, 0xa1, 0xb6, 0xc1, 0x2b, 0x6c, 0x14, 0x51, 0x45, 0x21, 0x90,
+ 0xad, 0xb0, 0x5b, 0x83, 0x2e, 0x4e, 0x4f, 0x6a, 0x3e, 0xcc, 0x3e, 0xd1,
+ 0xe6, 0xe4, 0xe7, 0xd2, 0xa6, 0xa2, 0xab, 0x99, 0x93, 0xca, 0x82, 0x8a,
+ 0x28, 0xa9, 0x28, 0x2a, 0x1f, 0xb3, 0x0f, 0xb4, 0x79, 0xb9, 0x39, 0xf4,
+ 0xa9, 0xa8, 0xa6, 0x9d, 0x84, 0xd5, 0xf7, 0x3e, 0x20, 0xff, 0x00, 0x87,
+ 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3,
+ 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a,
+ 0xbe, 0x17, 0xfe, 0xc6, 0xb2, 0xff, 0x00, 0x9f, 0x64, 0xa7, 0x47, 0xa2,
+ 0x59, 0x31, 0xe6, 0xd9, 0x31, 0x45, 0x8e, 0x9e, 0x44, 0x7d, 0xcd, 0xff,
+ 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
+ 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
+ 0x15, 0x7c, 0x3b, 0xfd, 0x87, 0x61, 0xff, 0x00, 0x3e, 0xb1, 0xfe, 0x54,
+ 0xd9, 0x34, 0x6b, 0x05, 0x1c, 0x5b, 0x47, 0x9f, 0xa5, 0x16, 0x0e, 0x44,
+ 0x7d, 0xc9, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7,
+ 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb,
+ 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x2f, 0xfd, 0x8d, 0x65, 0xff, 0x00, 0x3e,
+ 0xc9, 0xf9, 0x53, 0xe3, 0xd1, 0x2c, 0x4f, 0x26, 0xd9, 0x31, 0xf4, 0xa2,
+ 0xc1, 0xc8, 0x8f, 0xb9, 0x7f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
+ 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, 0xa6, 0xf8,
+ 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, 0x87, 0x7f,
+ 0xb0, 0xec, 0x3f, 0xe7, 0xd6, 0x3f, 0xca, 0x99, 0x26, 0x8d, 0x62, 0x38,
+ 0x16, 0xc9, 0x9a, 0x2c, 0x1c, 0x88, 0xfb, 0x97, 0xfe, 0x1e, 0x9b, 0xe1,
+ 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0x8f, 0xf8, 0x7a, 0x6f,
+ 0x85, 0xbf, 0xe8, 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0xf8, 0x5f, 0xfb,
+ 0x1a, 0xcb, 0xfe, 0x7d, 0x93, 0xf2, 0xa9, 0x23, 0xd1, 0x2c, 0x48, 0xc9,
+ 0xb6, 0x4f, 0xca, 0x8b, 0x07, 0x22, 0x3e, 0xe4, 0xff, 0x00, 0x87, 0xa6,
+ 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe,
+ 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe,
+ 0x1d, 0xfe, 0xc3, 0xb0, 0xff, 0x00, 0x9f, 0x58, 0xff, 0x00, 0x2a, 0x64,
+ 0x9a, 0x35, 0x88, 0x38, 0x16, 0xc9, 0xf9, 0x51, 0x60, 0xe4, 0x47, 0xdc,
+ 0xbf, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
+ 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff,
+ 0x00, 0x81, 0x51, 0x57, 0xc2, 0xff, 0x00, 0xd8, 0xd6, 0x5f, 0xf3, 0xec,
+ 0x9f, 0x95, 0x48, 0x9a, 0x1d, 0x89, 0x19, 0x36, 0xc9, 0xf9, 0x51, 0x60,
+ 0xe4, 0x47, 0xdc, 0x9f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac,
+ 0x7f, 0xe0, 0x54, 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42,
+ 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, 0x57, 0xc3, 0xbf, 0xd8, 0x76, 0x1f,
+ 0xf3, 0xeb, 0x1f, 0xe5, 0x51, 0xbe, 0x8d, 0x63, 0x9c, 0x0b, 0x64, 0xfc,
+ 0xa8, 0xb0, 0x72, 0x23, 0xee, 0x6f, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8,
+ 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
+ 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x7f, 0xec,
+ 0x7b, 0x2f, 0xf9, 0xf6, 0x4f, 0xca, 0x9b, 0x2e, 0x9d, 0xa7, 0xdb, 0xed,
+ 0x53, 0x67, 0xe6, 0xc8, 0xc0, 0x90, 0xb1, 0xae, 0x4e, 0x3d, 0x68, 0xb0,
+ 0x72, 0x23, 0xee, 0xaf, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, 0x47, 0xd6,
+ 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1,
+ 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x1f, 0x23, 0x4b, 0xd8,
+ 0x1b, 0xec, 0x44, 0x8d, 0xbb, 0x9b, 0x09, 0xf7, 0x06, 0x48, 0xc9, 0xe7,
+ 0xd8, 0xfe, 0x54, 0x3d, 0x9e, 0x9e, 0x2e, 0x04, 0x66, 0xcb, 0x60, 0x27,
+ 0x68, 0x72, 0xbf, 0x29, 0x3f, 0x5c, 0xd1, 0x60, 0xe4, 0x47, 0xdd, 0xdf,
+ 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x54,
+ 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00,
+ 0x81, 0x51, 0x57, 0xc1, 0x82, 0xdf, 0x4e, 0x1b, 0xb7, 0xd9, 0x98, 0x88,
+ 0x19, 0xda, 0xeb, 0xc9, 0xe7, 0x1c, 0x73, 0xea, 0x45, 0x4d, 0x1d, 0x9e,
+ 0x9a, 0x46, 0x0d, 0x91, 0x12, 0x02, 0x41, 0x8c, 0xaf, 0xcd, 0x9c, 0x67,
+ 0xd7, 0xd2, 0x8b, 0x07, 0x22, 0x3e, 0xed, 0xff, 0x00, 0x87, 0xa6, 0xf8,
+ 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e,
+ 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x16,
+ 0xb7, 0xd3, 0xf4, 0xeb, 0x86, 0x65, 0xfb, 0x1f, 0x96, 0xea, 0x01, 0x2b,
+ 0x22, 0xe0, 0xe0, 0xf7, 0xa7, 0x36, 0x8f, 0x62, 0x49, 0xc5, 0xb2, 0x62,
+ 0x8b, 0x07, 0x22, 0x3e, 0xe7, 0xff, 0x00, 0x87, 0xa6, 0xf8, 0x5b, 0xfe,
+ 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, 0x9b, 0xe1,
+ 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x17, 0xfe, 0xc6,
+ 0xb2, 0x3f, 0xf2, 0xec, 0x95, 0x30, 0xd0, 0xec, 0x40, 0xff, 0x00, 0x8f,
+ 0x64, 0xa2, 0xc1, 0xc8, 0x8f, 0xb8, 0xbf, 0xe1, 0xe9, 0xbe, 0x16, 0xff,
+ 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87,
+ 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf,
+ 0x87, 0x7f, 0xb1, 0x2c, 0x07, 0xfc, 0xba, 0xc7, 0xf9, 0x54, 0x27, 0x47,
+ 0xb2, 0x27, 0xfe, 0x3d, 0x92, 0x8b, 0x07, 0x22, 0x3e, 0xe8, 0xff, 0x00,
+ 0x87, 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2,
+ 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a,
+ 0x8a, 0xbe, 0x17, 0x1a, 0x2d, 0x91, 0x38, 0xfb, 0x32, 0x54, 0xdf, 0xd8,
+ 0x76, 0x1f, 0xf3, 0xea, 0x9f, 0x95, 0x16, 0x0e, 0x44, 0x7d, 0xc5, 0xff,
+ 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45,
+ 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15,
+ 0x15, 0x7c, 0x3a, 0x74, 0x4b, 0x00, 0x33, 0xf6, 0x58, 0xff, 0x00, 0x2a,
+ 0x84, 0xe8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x47,
+ 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15,
+ 0x1f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54,
+ 0x55, 0xf0, 0xc2, 0xe8, 0xb6, 0x4c, 0x40, 0xfb, 0x32, 0x54, 0xbf, 0xd8,
+ 0x76, 0x1f, 0xf3, 0xea, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x17, 0xfc, 0x3d,
+ 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x1f, 0xf0,
+ 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x55, 0xf0,
+ 0xe3, 0x68, 0xb6, 0x0a, 0x09, 0xfb, 0x2c, 0x7f, 0x95, 0x45, 0xfd, 0x8d,
+ 0x65, 0xff, 0x00, 0x3e, 0xc9, 0x45, 0x83, 0x91, 0x1f, 0x74, 0x7f, 0xc3,
+ 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51,
+ 0x51, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe,
+ 0x05, 0x45, 0x5f, 0x0c, 0x26, 0x89, 0x64, 0xcd, 0xff, 0x00, 0x1e, 0xc9,
+ 0x52, 0xff, 0x00, 0x61, 0xd8, 0x7f, 0xcf, 0xac, 0x7f, 0x95, 0x16, 0x0e,
+ 0x44, 0x7d, 0xc5, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa,
+ 0xc7, 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23,
+ 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x38, 0xfa, 0x2d, 0x82, 0xaf, 0xfc,
+ 0x7a, 0xc7, 0xf9, 0x54, 0x5f, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58,
+ 0x39, 0x11, 0x72, 0xa7, 0x45, 0xda, 0xb8, 0xa8, 0xe3, 0x5c, 0xb6, 0x7d,
+ 0x2a, 0x5a, 0xa2, 0xc2, 0xa1, 0x66, 0xdc, 0xd9, 0xa7, 0xc8, 0xd8, 0x18,
+ 0xf5, 0xa8, 0xa8, 0x01, 0x40, 0xc9, 0xa9, 0xd4, 0x6d, 0x18, 0xa8, 0xe2,
+ 0x5e, 0x73, 0x52, 0x50, 0x00, 0x4e, 0x05, 0x40, 0xc7, 0x71, 0xcd, 0x49,
+ 0x2b, 0x71, 0x8a, 0x8a, 0x80, 0x14, 0x0c, 0x9c, 0x54, 0xe0, 0x60, 0x62,
+ 0xa3, 0x89, 0x7b, 0xd4, 0x94, 0x00, 0x13, 0x80, 0x4d, 0x40, 0x4e, 0x4e,
+ 0x69, 0xf2, 0xb7, 0x6a, 0x8e, 0x80, 0x15, 0x46, 0xe3, 0x8a, 0x9e, 0x99,
+ 0x1a, 0xe0, 0x67, 0xd6, 0x9f, 0x40, 0x08, 0xc7, 0x68, 0xcd, 0x41, 0x4f,
+ 0x91, 0xb2, 0x71, 0xe9, 0x4c, 0xa0, 0x07, 0x22, 0xee, 0x6f, 0x6a, 0x27,
+ 0xb5, 0x59, 0xa4, 0x49, 0x37, 0xbc, 0x6e, 0xbc, 0x06, 0x43, 0x8c, 0x8f,
+ 0x43, 0xed, 0x52, 0x46, 0xb8, 0x5f, 0x73, 0x4e, 0xa0, 0x0a, 0xa6, 0xca,
+ 0x38, 0xa2, 0x95, 0x41, 0x6f, 0xde, 0x2e, 0xc3, 0xcf, 0x6c, 0x93, 0xff,
+ 0x00, 0xb3, 0x1a, 0x87, 0xec, 0x60, 0xca, 0xae, 0xd2, 0x48, 0xe1, 0x4e,
+ 0x42, 0x12, 0x36, 0x83, 0xf9, 0x55, 0xa9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d,
+ 0x00, 0x57, 0x8b, 0x49, 0xb7, 0x0c, 0xfb, 0x53, 0xcb, 0x0c, 0x00, 0x21,
+ 0x38, 0xe8, 0x72, 0x0f, 0xd6, 0xa6, 0x5d, 0x39, 0x17, 0x90, 0xf2, 0x79,
+ 0x99, 0x2c, 0x64, 0x27, 0x2c, 0x4e, 0xdc, 0x7e, 0x82, 0xac, 0xa2, 0xed,
+ 0x5a, 0x5a, 0x00, 0xad, 0x1d, 0xaa, 0xdb, 0x16, 0x6f, 0x31, 0xe5, 0x91,
+ 0xc0, 0x05, 0xe4, 0x39, 0x38, 0x1d, 0xbf, 0x5a, 0x5a, 0x73, 0xb6, 0xe6,
+ 0xa6, 0xf5, 0xa0, 0x07, 0xc4, 0xb9, 0x39, 0xf4, 0xa9, 0x69, 0x14, 0x6d,
+ 0x18, 0xa5, 0xe9, 0x40, 0x0c, 0x95, 0xb0, 0x31, 0x51, 0x52, 0xb1, 0xdc,
+ 0x49, 0xa0, 0x0c, 0x9c, 0x50, 0x03, 0xe2, 0x5e, 0xf5, 0x25, 0x00, 0x60,
+ 0x62, 0x82, 0x70, 0x33, 0x40, 0x11, 0xca, 0xdd, 0xaa, 0x3a, 0x52, 0x72,
+ 0x73, 0x42, 0x8d, 0xc4, 0x0a, 0x00, 0x92, 0x25, 0xc0, 0xcd, 0x3e, 0x8e,
+ 0x94, 0x8c, 0x76, 0x82, 0x68, 0x02, 0x39, 0x5b, 0x27, 0x1e, 0x94, 0xca,
+ 0x3a, 0xd3, 0x91, 0x77, 0x35, 0x00, 0x49, 0x1a, 0xe1, 0x7d, 0xcd, 0x3a,
+ 0x8a, 0x47, 0x6d, 0xab, 0x40, 0x11, 0xc8, 0xd9, 0x6f, 0x61, 0x4c, 0xa2,
+ 0x9d, 0x1a, 0xee, 0x6f, 0x61, 0x40, 0x1f, 0xff, 0xd9
+};
diff --git a/services/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp
new file mode 100644
index 0000000..6749899
--- /dev/null
+++ b/services/camera/libcameraservice/FakeCamera.cpp
@@ -0,0 +1,430 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "FakeCamera"
+#include <utils/Log.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <utils/String8.h>
+
+#include "FakeCamera.h"
+
+
+namespace android {
+
+// TODO: All this rgb to yuv should probably be in a util class.
+
+// TODO: I think something is wrong in this class because the shadow is kBlue
+// and the square color should alternate between kRed and kGreen. However on the
+// emulator screen these are all shades of gray. Y seems ok but the U and V are
+// probably not.
+
+static int tables_initialized = 0;
+uint8_t *gYTable, *gCbTable, *gCrTable;
+
+static int
+clamp(int x)
+{
+ if (x > 255) return 255;
+ if (x < 0) return 0;
+ return x;
+}
+
+/* the equation used by the video code to translate YUV to RGB looks like this
+ *
+ * Y = (Y0 - 16)*k0
+ * Cb = Cb0 - 128
+ * Cr = Cr0 - 128
+ *
+ * G = ( Y - k1*Cr - k2*Cb )
+ * R = ( Y + k3*Cr )
+ * B = ( Y + k4*Cb )
+ *
+ */
+
+static const double k0 = 1.164;
+static const double k1 = 0.813;
+static const double k2 = 0.391;
+static const double k3 = 1.596;
+static const double k4 = 2.018;
+
+/* let's try to extract the value of Y
+ *
+ * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 )
+ *
+ * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4)
+ * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16
+ *
+ * let define:
+ * kYr = k1/k3
+ * kYb = k2/k4
+ * kYy = k0 * ( 1 + kYr + kYb )
+ *
+ * we have:
+ * Y = ( G + kYr*R + kYb*B )
+ * Y0 = clamp[ Y/kYy + 16 ]
+ */
+
+static const double kYr = k1/k3;
+static const double kYb = k2/k4;
+static const double kYy = k0*( 1. + kYr + kYb );
+
+static void
+initYtab( void )
+{
+ const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 );
+ int i;
+
+ gYTable = (uint8_t *)malloc(imax);
+
+ for(i=0; i<imax; i++) {
+ int x = (int)(i/kYy + 16.5);
+ if (x < 16) x = 16;
+ else if (x > 235) x = 235;
+ gYTable[i] = (uint8_t) x;
+ }
+}
+
+/*
+ * the source is RGB565, so adjust for 8-bit range of input values:
+ *
+ * G = (pixels >> 3) & 0xFC;
+ * R = (pixels >> 8) & 0xF8;
+ * B = (pixels & 0x1f) << 3;
+ *
+ * R2 = (pixels >> 11) R = R2*8
+ * B2 = (pixels & 0x1f) B = B2*8
+ *
+ * kYr*R = kYr2*R2 => kYr2 = kYr*8
+ * kYb*B = kYb2*B2 => kYb2 = kYb*8
+ *
+ * we want to use integer multiplications:
+ *
+ * SHIFT1 = 9
+ *
+ * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1)
+ *
+ * ALPHA = kYr*(1 << (SHIFT1+3))
+ * BETA = kYb*(1 << (SHIFT1+3))
+ */
+
+static const int SHIFT1 = 9;
+static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 );
+static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 );
+
+/*
+ * now let's try to get the values of Cb and Cr
+ *
+ * R-B = (k3*Cr - k4*Cb)
+ *
+ * k3*Cr = k4*Cb + (R-B)
+ * k4*Cb = k3*Cr - (R-B)
+ *
+ * R-G = (k1+k3)*Cr + k2*Cb
+ * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0)
+ * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B)
+ *
+ * kRr*Cr = (R-G) + kYb*(R-B)
+ *
+ * Cr = ((R-G) + kYb*(R-B))/kRr
+ * Cr0 = clamp(Cr + 128)
+ */
+
+static const double kRr = (k1 + k3 + k2*k3/k4);
+
+static void
+initCrtab( void )
+{
+ uint8_t *pTable;
+ int i;
+
+ gCrTable = (uint8_t *)malloc(768*2);
+
+ pTable = gCrTable + 384;
+ for(i=-384; i<384; i++)
+ pTable[i] = (uint8_t) clamp( i/kRr + 128.5 );
+}
+
+/*
+ * B-G = (k2 + k4)*Cb + k1*Cr
+ * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B))
+ * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B)
+ *
+ * kBb*Cb = (B-G) - kYr*(R-B)
+ *
+ * Cb = ((B-G) - kYr*(R-B))/kBb
+ * Cb0 = clamp(Cb + 128)
+ *
+ */
+
+static const double kBb = (k2 + k4 + k1*k4/k3);
+
+static void
+initCbtab( void )
+{
+ uint8_t *pTable;
+ int i;
+
+ gCbTable = (uint8_t *)malloc(768*2);
+
+ pTable = gCbTable + 384;
+ for(i=-384; i<384; i++)
+ pTable[i] = (uint8_t) clamp( i/kBb + 128.5 );
+}
+
+/*
+ * SHIFT2 = 16
+ *
+ * DELTA = kYb*(1 << SHIFT2)
+ * GAMMA = kYr*(1 << SHIFT2)
+ */
+
+static const int SHIFT2 = 16;
+static const int DELTA = kYb*(1 << SHIFT2);
+static const int GAMMA = kYr*(1 << SHIFT2);
+
+int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16,uint8_t *yuv422,uint32_t *param,uint8_t *table[])
+{
+ uint16_t *inputRGB = (uint16_t*)rgb16;
+ uint8_t *outYUV = yuv422;
+ int32_t width_dst = param[0];
+ int32_t height_dst = param[1];
+ int32_t pitch_dst = param[2];
+ int32_t mheight_dst = param[3];
+ int32_t pitch_src = param[4];
+ uint8_t *y_tab = table[0];
+ uint8_t *cb_tab = table[1];
+ uint8_t *cr_tab = table[2];
+
+ int32_t size16 = pitch_dst*mheight_dst;
+ int32_t i,j,count;
+ int32_t ilimit,jlimit;
+ uint8_t *tempY,*tempU,*tempV;
+ uint16_t pixels;
+ int tmp;
+uint32_t temp;
+
+ tempY = outYUV;
+ tempU = outYUV + (height_dst * pitch_dst);
+ tempV = tempU + 1;
+
+ jlimit = height_dst;
+ ilimit = width_dst;
+
+ for(j=0; j<jlimit; j+=1)
+ {
+ for (i=0; i<ilimit; i+=2)
+ {
+ int32_t G_ds = 0, B_ds = 0, R_ds = 0;
+ uint8_t y0, y1, u, v;
+
+ pixels = inputRGB[i];
+ temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
+ y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
+
+ G_ds += (pixels>>1) & 0x03E0;
+ B_ds += (pixels<<5) & 0x03E0;
+ R_ds += (pixels>>6) & 0x03E0;
+
+ pixels = inputRGB[i+1];
+ temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) );
+ y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)];
+
+ G_ds += (pixels>>1) & 0x03E0;
+ B_ds += (pixels<<5) & 0x03E0;
+ R_ds += (pixels>>6) & 0x03E0;
+
+ R_ds >>= 1;
+ B_ds >>= 1;
+ G_ds >>= 1;
+
+ tmp = R_ds - B_ds;
+
+ u = cb_tab[(((B_ds-G_ds)<<SHIFT2) - GAMMA*tmp)>>(SHIFT2+2)];
+ v = cr_tab[(((R_ds-G_ds)<<SHIFT2) + DELTA*tmp)>>(SHIFT2+2)];
+
+ tempY[0] = y0;
+ tempY[1] = y1;
+ tempU[0] = u;
+ tempV[0] = v;
+
+ tempY += 2;
+ tempU += 2;
+ tempV += 2;
+ }
+
+ inputRGB += pitch_src;
+ }
+
+ return 1;
+}
+
+#define min(a,b) ((a)<(b)?(a):(b))
+#define max(a,b) ((a)>(b)?(a):(b))
+
+static void convert_rgb16_to_yuv422(uint8_t *rgb, uint8_t *yuv, int width, int height)
+{
+ if (!tables_initialized) {
+ initYtab();
+ initCrtab();
+ initCbtab();
+ tables_initialized = 1;
+ }
+
+ uint32_t param[6];
+ param[0] = (uint32_t) width;
+ param[1] = (uint32_t) height;
+ param[2] = (uint32_t) width;
+ param[3] = (uint32_t) height;
+ param[4] = (uint32_t) width;
+ param[5] = (uint32_t) 0;
+
+ uint8_t *table[3];
+ table[0] = gYTable;
+ table[1] = gCbTable + 384;
+ table[2] = gCrTable + 384;
+
+ ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table);
+}
+
+const int FakeCamera::kRed;
+const int FakeCamera::kGreen;
+const int FakeCamera::kBlue;
+
+FakeCamera::FakeCamera(int width, int height)
+ : mTmpRgb16Buffer(0)
+{
+ setSize(width, height);
+}
+
+FakeCamera::~FakeCamera()
+{
+ delete[] mTmpRgb16Buffer;
+}
+
+void FakeCamera::setSize(int width, int height)
+{
+ mWidth = width;
+ mHeight = height;
+ mCounter = 0;
+ mCheckX = 0;
+ mCheckY = 0;
+
+ // This will cause it to be reallocated on the next call
+ // to getNextFrameAsYuv422().
+ delete[] mTmpRgb16Buffer;
+ mTmpRgb16Buffer = 0;
+}
+
+void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer)
+{
+ int size = mWidth / 10;
+
+ drawCheckerboard(buffer, size);
+
+ int x = ((mCounter*3)&255);
+ if(x>128) x = 255 - x;
+ int y = ((mCounter*5)&255);
+ if(y>128) y = 255 - y;
+
+ drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue);
+
+ mCounter++;
+}
+
+void FakeCamera::getNextFrameAsYuv422(uint8_t *buffer)
+{
+ if (mTmpRgb16Buffer == 0)
+ mTmpRgb16Buffer = new uint16_t[mWidth * mHeight];
+
+ getNextFrameAsRgb565(mTmpRgb16Buffer);
+ convert_rgb16_to_yuv422((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight);
+}
+
+void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow)
+{
+ int square_xstop, square_ystop, shadow_xstop, shadow_ystop;
+
+ square_xstop = min(mWidth, x+size);
+ square_ystop = min(mHeight, y+size);
+ shadow_xstop = min(mWidth, x+size+(size/4));
+ shadow_ystop = min(mHeight, y+size+(size/4));
+
+ // Do the shadow.
+ uint16_t *sh = &dst[(y+(size/4))*mWidth];
+ for (int j = y + (size/4); j < shadow_ystop; j++) {
+ for (int i = x + (size/4); i < shadow_xstop; i++) {
+ sh[i] &= shadow;
+ }
+ sh += mWidth;
+ }
+
+ // Draw the square.
+ uint16_t *sq = &dst[y*mWidth];
+ for (int j = y; j < square_ystop; j++) {
+ for (int i = x; i < square_xstop; i++) {
+ sq[i] = color;
+ }
+ sq += mWidth;
+ }
+}
+
+void FakeCamera::drawCheckerboard(uint16_t *dst, int size)
+{
+ bool black = true;
+
+ if((mCheckX/size)&1)
+ black = false;
+ if((mCheckY/size)&1)
+ black = !black;
+
+ int county = mCheckY%size;
+ int checkxremainder = mCheckX%size;
+
+ for(int y=0;y<mHeight;y++) {
+ int countx = checkxremainder;
+ bool current = black;
+ for(int x=0;x<mWidth;x++) {
+ dst[y*mWidth+x] = current?0:0xffff;
+ if(countx++ >= size) {
+ countx=0;
+ current = !current;
+ }
+ }
+ if(county++ >= size) {
+ county=0;
+ black = !black;
+ }
+ }
+ mCheckX += 3;
+ mCheckY++;
+}
+
+
+void FakeCamera::dump(int fd) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY);
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+}
+
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h
new file mode 100644
index 0000000..f7f8803
--- /dev/null
+++ b/services/camera/libcameraservice/FakeCamera.h
@@ -0,0 +1,67 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_HARDWARE_FAKECAMERA_H
+#define ANDROID_HARDWARE_FAKECAMERA_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+namespace android {
+
+/*
+ * FakeCamera is used in the CameraHardwareStub to provide a fake video feed
+ * when the system does not have a camera in hardware.
+ * The fake video is a moving black and white checkerboard background with a
+ * bouncing gray square in the foreground.
+ * This class is not thread-safe.
+ *
+ * TODO: Since the major methods provides a raw/uncompressed video feed, rename
+ * this class to RawVideoSource.
+ */
+
+class FakeCamera {
+public:
+ FakeCamera(int width, int height);
+ ~FakeCamera();
+
+ void setSize(int width, int height);
+ void getNextFrameAsYuv422(uint8_t *buffer);
+ // Write to the fd a string representing the current state.
+ void dump(int fd) const;
+
+private:
+ // TODO: remove the uint16_t buffer param everywhere since it is a field of
+ // this class.
+ void getNextFrameAsRgb565(uint16_t *buffer);
+
+ void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow);
+ void drawCheckerboard(uint16_t *buffer, int size);
+
+ static const int kRed = 0xf800;
+ static const int kGreen = 0x07c0;
+ static const int kBlue = 0x003e;
+
+ int mWidth, mHeight;
+ int mCounter;
+ int mCheckX, mCheckY;
+ uint16_t *mTmpRgb16Buffer;
+};
+
+}; // namespace android
+
+#endif // ANDROID_HARDWARE_FAKECAMERA_H
diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk
new file mode 100644
index 0000000..9bb190a
--- /dev/null
+++ b/services/camera/tests/CameraServiceTest/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= CameraServiceTest.cpp
+
+LOCAL_MODULE:= CameraServiceTest
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_C_INCLUDES += \
+ frameworks/base/libs
+
+LOCAL_CFLAGS :=
+
+LOCAL_SHARED_LIBRARIES += \
+ libbinder \
+ libcutils \
+ libutils \
+ libui \
+ libcamera_client \
+ libsurfaceflinger_client
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
new file mode 100644
index 0000000..9fc795b
--- /dev/null
+++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp
@@ -0,0 +1,849 @@
+#define LOG_TAG "CameraServiceTest"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <surfaceflinger/ISurface.h>
+#include <camera/Camera.h>
+#include <camera/CameraParameters.h>
+#include <ui/GraphicBuffer.h>
+#include <camera/ICamera.h>
+#include <camera/ICameraClient.h>
+#include <camera/ICameraService.h>
+#include <ui/Overlay.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+using namespace android;
+
+//
+// Assertion and Logging utilities
+//
+#define INFO(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+ LOGD(__VA_ARGS__); \
+ } while(0)
+
+void assert_fail(const char *file, int line, const char *func, const char *expr) {
+ INFO("assertion failed at file %s, line %d, function %s:",
+ file, line, func);
+ INFO("%s", expr);
+ exit(1);
+}
+
+void assert_eq_fail(const char *file, int line, const char *func,
+ const char *expr, int actual) {
+ INFO("assertion failed at file %s, line %d, function %s:",
+ file, line, func);
+ INFO("(expected) %s != (actual) %d", expr, actual);
+ exit(1);
+}
+
+#define ASSERT(e) \
+ do { \
+ if (!(e)) \
+ assert_fail(__FILE__, __LINE__, __func__, #e); \
+ } while(0)
+
+#define ASSERT_EQ(expected, actual) \
+ do { \
+ int _x = (actual); \
+ if (_x != (expected)) \
+ assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \
+ } while(0)
+
+//
+// Holder service for pass objects between processes.
+//
+class IHolder : public IInterface {
+protected:
+ enum {
+ HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION,
+ HOLDER_GET,
+ HOLDER_CLEAR
+ };
+public:
+ DECLARE_META_INTERFACE(Holder);
+
+ virtual void put(sp<IBinder> obj) = 0;
+ virtual sp<IBinder> get() = 0;
+ virtual void clear() = 0;
+};
+
+class BnHolder : public BnInterface<IHolder> {
+ virtual status_t onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+class BpHolder : public BpInterface<IHolder> {
+public:
+ BpHolder(const sp<IBinder>& impl)
+ : BpInterface<IHolder>(impl) {
+ }
+
+ virtual void put(sp<IBinder> obj) {
+ Parcel data, reply;
+ data.writeStrongBinder(obj);
+ remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual sp<IBinder> get() {
+ Parcel data, reply;
+ remote()->transact(HOLDER_GET, data, &reply);
+ return reply.readStrongBinder();
+ }
+
+ virtual void clear() {
+ Parcel data, reply;
+ remote()->transact(HOLDER_CLEAR, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder");
+
+status_t BnHolder::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+ switch(code) {
+ case HOLDER_PUT: {
+ put(data.readStrongBinder());
+ return NO_ERROR;
+ } break;
+ case HOLDER_GET: {
+ reply->writeStrongBinder(get());
+ return NO_ERROR;
+ } break;
+ case HOLDER_CLEAR: {
+ clear();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+class HolderService : public BnHolder {
+ virtual void put(sp<IBinder> obj) {
+ mObj = obj;
+ }
+ virtual sp<IBinder> get() {
+ return mObj;
+ }
+ virtual void clear() {
+ mObj.clear();
+ }
+private:
+ sp<IBinder> mObj;
+};
+
+//
+// A mock CameraClient
+//
+class MCameraClient : public BnCameraClient {
+public:
+ virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
+ virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
+ virtual void dataCallbackTimestamp(nsecs_t timestamp,
+ int32_t msgType, const sp<IMemory>& data) {}
+
+ // new functions
+ void clearStat();
+ enum OP { EQ, GE, LE, GT, LT };
+ void assertNotify(int32_t msgType, OP op, int count);
+ void assertData(int32_t msgType, OP op, int count);
+ void waitNotify(int32_t msgType, OP op, int count);
+ void waitData(int32_t msgType, OP op, int count);
+ void assertDataSize(int32_t msgType, OP op, int dataSize);
+
+ void setReleaser(ICamera *releaser) {
+ mReleaser = releaser;
+ }
+private:
+ Mutex mLock;
+ Condition mCond;
+ DefaultKeyedVector<int32_t, int> mNotifyCount;
+ DefaultKeyedVector<int32_t, int> mDataCount;
+ DefaultKeyedVector<int32_t, int> mDataSize;
+ bool test(OP op, int v1, int v2);
+
+ ICamera *mReleaser;
+};
+
+void MCameraClient::clearStat() {
+ Mutex::Autolock _l(mLock);
+ mNotifyCount.clear();
+ mDataCount.clear();
+ mDataSize.clear();
+}
+
+bool MCameraClient::test(OP op, int v1, int v2) {
+ switch (op) {
+ case EQ: return v1 == v2;
+ case GT: return v1 > v2;
+ case LT: return v1 < v2;
+ case GE: return v1 >= v2;
+ case LE: return v1 <= v2;
+ default: ASSERT(0); break;
+ }
+ return false;
+}
+
+void MCameraClient::assertNotify(int32_t msgType, OP op, int count) {
+ Mutex::Autolock _l(mLock);
+ int v = mNotifyCount.valueFor(msgType);
+ ASSERT(test(op, v, count));
+}
+
+void MCameraClient::assertData(int32_t msgType, OP op, int count) {
+ Mutex::Autolock _l(mLock);
+ int v = mDataCount.valueFor(msgType);
+ ASSERT(test(op, v, count));
+}
+
+void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) {
+ Mutex::Autolock _l(mLock);
+ int v = mDataSize.valueFor(msgType);
+ ASSERT(test(op, v, dataSize));
+}
+
+void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+ INFO(__func__);
+ Mutex::Autolock _l(mLock);
+ ssize_t i = mNotifyCount.indexOfKey(msgType);
+ if (i < 0) {
+ mNotifyCount.add(msgType, 1);
+ } else {
+ ++mNotifyCount.editValueAt(i);
+ }
+ mCond.signal();
+}
+
+void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) {
+ INFO(__func__);
+ int dataSize = data->size();
+ INFO("data type = %d, size = %d", msgType, dataSize);
+ Mutex::Autolock _l(mLock);
+ ssize_t i = mDataCount.indexOfKey(msgType);
+ if (i < 0) {
+ mDataCount.add(msgType, 1);
+ mDataSize.add(msgType, dataSize);
+ } else {
+ ++mDataCount.editValueAt(i);
+ mDataSize.editValueAt(i) = dataSize;
+ }
+ mCond.signal();
+
+ if (msgType == CAMERA_MSG_VIDEO_FRAME) {
+ ASSERT(mReleaser != NULL);
+ mReleaser->releaseRecordingFrame(data);
+ }
+}
+
+void MCameraClient::waitNotify(int32_t msgType, OP op, int count) {
+ INFO("waitNotify: %d, %d, %d", msgType, op, count);
+ Mutex::Autolock _l(mLock);
+ while (true) {
+ int v = mNotifyCount.valueFor(msgType);
+ if (test(op, v, count)) {
+ break;
+ }
+ mCond.wait(mLock);
+ }
+}
+
+void MCameraClient::waitData(int32_t msgType, OP op, int count) {
+ INFO("waitData: %d, %d, %d", msgType, op, count);
+ Mutex::Autolock _l(mLock);
+ while (true) {
+ int v = mDataCount.valueFor(msgType);
+ if (test(op, v, count)) {
+ break;
+ }
+ mCond.wait(mLock);
+ }
+}
+
+//
+// A mock Surface
+//
+class MSurface : public BnSurface {
+public:
+ virtual status_t registerBuffers(const BufferHeap& buffers);
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+ virtual sp<OverlayRef> createOverlay(
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage);
+
+ // new functions
+ void clearStat();
+ void waitUntil(int c0, int c1, int c2);
+
+private:
+ // check callback count
+ Condition mCond;
+ Mutex mLock;
+ int registerBuffersCount;
+ int postBufferCount;
+ int unregisterBuffersCount;
+};
+
+status_t MSurface::registerBuffers(const BufferHeap& buffers) {
+ INFO(__func__);
+ Mutex::Autolock _l(mLock);
+ ++registerBuffersCount;
+ mCond.signal();
+ return NO_ERROR;
+}
+
+void MSurface::postBuffer(ssize_t offset) {
+ // INFO(__func__);
+ Mutex::Autolock _l(mLock);
+ ++postBufferCount;
+ mCond.signal();
+}
+
+void MSurface::unregisterBuffers() {
+ INFO(__func__);
+ Mutex::Autolock _l(mLock);
+ ++unregisterBuffersCount;
+ mCond.signal();
+}
+
+sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) {
+ INFO(__func__);
+ return NULL;
+}
+
+void MSurface::clearStat() {
+ Mutex::Autolock _l(mLock);
+ registerBuffersCount = 0;
+ postBufferCount = 0;
+ unregisterBuffersCount = 0;
+}
+
+void MSurface::waitUntil(int c0, int c1, int c2) {
+ INFO("waitUntil: %d %d %d", c0, c1, c2);
+ Mutex::Autolock _l(mLock);
+ while (true) {
+ if (registerBuffersCount >= c0 &&
+ postBufferCount >= c1 &&
+ unregisterBuffersCount >= c2) {
+ break;
+ }
+ mCond.wait(mLock);
+ }
+}
+
+sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format,
+ int32_t orientation) {
+ // We don't expect this to be called in current hardware.
+ ASSERT(0);
+ sp<OverlayRef> dummy;
+ return dummy;
+}
+
+//
+// Utilities to use the Holder service
+//
+sp<IHolder> getHolder() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT(sm != 0);
+ sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder"));
+ ASSERT(binder != 0);
+ sp<IHolder> holder = interface_cast<IHolder>(binder);
+ ASSERT(holder != 0);
+ return holder;
+}
+
+void putTempObject(sp<IBinder> obj) {
+ INFO(__func__);
+ getHolder()->put(obj);
+}
+
+sp<IBinder> getTempObject() {
+ INFO(__func__);
+ return getHolder()->get();
+}
+
+void clearTempObject() {
+ INFO(__func__);
+ getHolder()->clear();
+}
+
+//
+// Get a Camera Service
+//
+sp<ICameraService> getCameraService() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT(sm != 0);
+ sp<IBinder> binder = sm->getService(String16("media.camera"));
+ ASSERT(binder != 0);
+ sp<ICameraService> cs = interface_cast<ICameraService>(binder);
+ ASSERT(cs != 0);
+ return cs;
+}
+
+//
+// Various Connect Tests
+//
+void testConnect() {
+ INFO(__func__);
+ sp<ICameraService> cs = getCameraService();
+ sp<MCameraClient> cc = new MCameraClient();
+ sp<ICamera> c = cs->connect(cc);
+ ASSERT(c != 0);
+ c->disconnect();
+}
+
+void testAllowConnectOnceOnly() {
+ INFO(__func__);
+ sp<ICameraService> cs = getCameraService();
+ // Connect the first client.
+ sp<MCameraClient> cc = new MCameraClient();
+ sp<ICamera> c = cs->connect(cc);
+ ASSERT(c != 0);
+ // Same client -- ok.
+ ASSERT(cs->connect(cc) != 0);
+ // Different client -- not ok.
+ sp<MCameraClient> cc2 = new MCameraClient();
+ ASSERT(cs->connect(cc2) == 0);
+ c->disconnect();
+}
+
+void testReconnectFailed() {
+ INFO(__func__);
+ sp<ICamera> c = interface_cast<ICamera>(getTempObject());
+ sp<MCameraClient> cc2 = new MCameraClient();
+ ASSERT(c->connect(cc2) != NO_ERROR);
+}
+
+void testReconnectSuccess() {
+ INFO(__func__);
+ sp<ICamera> c = interface_cast<ICamera>(getTempObject());
+ sp<MCameraClient> cc = new MCameraClient();
+ ASSERT(c->connect(cc) == NO_ERROR);
+}
+
+void testLockFailed() {
+ INFO(__func__);
+ sp<ICamera> c = interface_cast<ICamera>(getTempObject());
+ ASSERT(c->lock() != NO_ERROR);
+}
+
+void testLockUnlockSuccess() {
+ INFO(__func__);
+ sp<ICamera> c = interface_cast<ICamera>(getTempObject());
+ ASSERT(c->lock() == NO_ERROR);
+ ASSERT(c->unlock() == NO_ERROR);
+}
+
+void testLockSuccess() {
+ INFO(__func__);
+ sp<ICamera> c = interface_cast<ICamera>(getTempObject());
+ ASSERT(c->lock() == NO_ERROR);
+}
+
+//
+// Run the connect tests in another process.
+//
+const char *gExecutable;
+
+struct FunctionTableEntry {
+ const char *name;
+ void (*func)();
+};
+
+FunctionTableEntry function_table[] = {
+#define ENTRY(x) {#x, &x}
+ ENTRY(testReconnectFailed),
+ ENTRY(testReconnectSuccess),
+ ENTRY(testLockUnlockSuccess),
+ ENTRY(testLockFailed),
+ ENTRY(testLockSuccess),
+#undef ENTRY
+};
+
+void runFunction(const char *tag) {
+ INFO("runFunction: %s", tag);
+ int entries = sizeof(function_table) / sizeof(function_table[0]);
+ for (int i = 0; i < entries; i++) {
+ if (strcmp(function_table[i].name, tag) == 0) {
+ (*function_table[i].func)();
+ return;
+ }
+ }
+ ASSERT(0);
+}
+
+void runInAnotherProcess(const char *tag) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ execlp(gExecutable, gExecutable, tag, NULL);
+ ASSERT(0);
+ } else {
+ int status;
+ ASSERT_EQ(pid, wait(&status));
+ ASSERT_EQ(0, status);
+ }
+}
+
+void testReconnect() {
+ INFO(__func__);
+ sp<ICameraService> cs = getCameraService();
+ sp<MCameraClient> cc = new MCameraClient();
+ sp<ICamera> c = cs->connect(cc);
+ ASSERT(c != 0);
+ // Reconnect to the same client -- ok.
+ ASSERT(c->connect(cc) == NO_ERROR);
+ // Reconnect to a different client (but the same pid) -- ok.
+ sp<MCameraClient> cc2 = new MCameraClient();
+ ASSERT(c->connect(cc2) == NO_ERROR);
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+}
+
+void testLockUnlock() {
+ sp<ICameraService> cs = getCameraService();
+ sp<MCameraClient> cc = new MCameraClient();
+ sp<ICamera> c = cs->connect(cc);
+ ASSERT(c != 0);
+ // We can lock as many times as we want.
+ ASSERT(c->lock() == NO_ERROR);
+ ASSERT(c->lock() == NO_ERROR);
+ // Lock from a different process -- not ok.
+ putTempObject(c->asBinder());
+ runInAnotherProcess("testLockFailed");
+ // Unlock then lock from a different process -- ok.
+ ASSERT(c->unlock() == NO_ERROR);
+ runInAnotherProcess("testLockUnlockSuccess");
+ // Unlock then lock from a different process -- ok.
+ runInAnotherProcess("testLockSuccess");
+ c->disconnect();
+ clearTempObject();
+}
+
+void testReconnectFromAnotherProcess() {
+ INFO(__func__);
+
+ sp<ICameraService> cs = getCameraService();
+ sp<MCameraClient> cc = new MCameraClient();
+ sp<ICamera> c = cs->connect(cc);
+ ASSERT(c != 0);
+ // Reconnect from a different process -- not ok.
+ putTempObject(c->asBinder());
+ runInAnotherProcess("testReconnectFailed");
+ // Unlock then reconnect from a different process -- ok.
+ ASSERT(c->unlock() == NO_ERROR);
+ runInAnotherProcess("testReconnectSuccess");
+ c->disconnect();
+ clearTempObject();
+}
+
+// We need to flush the command buffer after the reference
+// to ICamera is gone. The sleep is for the server to run
+// the destructor for it.
+static void flushCommands() {
+ IPCThreadState::self()->flushCommands();
+ usleep(200000); // 200ms
+}
+
+// Run a test case
+#define RUN(class_name) do { \
+ { \
+ INFO(#class_name); \
+ class_name instance; \
+ instance.run(); \
+ } \
+ flushCommands(); \
+} while(0)
+
+// Base test case after the the camera is connected.
+class AfterConnect {
+protected:
+ sp<ICameraService> cs;
+ sp<MCameraClient> cc;
+ sp<ICamera> c;
+
+ AfterConnect() {
+ cs = getCameraService();
+ cc = new MCameraClient();
+ c = cs->connect(cc);
+ ASSERT(c != 0);
+ }
+
+ ~AfterConnect() {
+ c.clear();
+ cc.clear();
+ cs.clear();
+ }
+};
+
+class TestSetPreviewDisplay : public AfterConnect {
+public:
+ void run() {
+ sp<MSurface> surface = new MSurface();
+ ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestStartPreview : public AfterConnect {
+public:
+ void run() {
+ sp<MSurface> surface = new MSurface();
+ ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
+
+ ASSERT(c->startPreview() == NO_ERROR);
+ ASSERT(c->previewEnabled() == true);
+
+ surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer
+ surface->clearStat();
+
+ c->disconnect();
+ // TODO: CameraService crashes for this. Fix it.
+#if 0
+ sp<MSurface> another_surface = new MSurface();
+ c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers
+ // is called.
+ surface->waitUntil(0, 0, 1); // needs unregisterBuffers
+#endif
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestStartPreviewWithoutDisplay : AfterConnect {
+public:
+ void run() {
+ ASSERT(c->startPreview() == NO_ERROR);
+ ASSERT(c->previewEnabled() == true);
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+// Base test case after the the camera is connected and the preview is started.
+class AfterStartPreview : public AfterConnect {
+protected:
+ sp<MSurface> surface;
+
+ AfterStartPreview() {
+ surface = new MSurface();
+ ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
+ ASSERT(c->startPreview() == NO_ERROR);
+ }
+
+ ~AfterStartPreview() {
+ surface.clear();
+ }
+};
+
+class TestAutoFocus : public AfterStartPreview {
+public:
+ void run() {
+ cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0);
+ c->autoFocus();
+ cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1);
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestStopPreview : public AfterStartPreview {
+public:
+ void run() {
+ ASSERT(c->previewEnabled() == true);
+ c->stopPreview();
+ ASSERT(c->previewEnabled() == false);
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestTakePicture: public AfterStartPreview {
+public:
+ void run() {
+ ASSERT(c->takePicture() == NO_ERROR);
+ cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
+ cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
+ cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
+ c->stopPreview();
+#if 1 // TODO: It crashes if we don't have this. Fix it.
+ usleep(100000);
+#endif
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestTakeMultiplePictures: public AfterStartPreview {
+public:
+ void run() {
+ for (int i = 0; i < 10; i++) {
+ cc->clearStat();
+ ASSERT(c->takePicture() == NO_ERROR);
+ cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1);
+ cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
+ cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
+ usleep(100000); // 100ms
+ }
+ c->disconnect();
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ }
+};
+
+class TestGetParameters: public AfterStartPreview {
+public:
+ void run() {
+ String8 param_str = c->getParameters();
+ INFO(param_str);
+ }
+};
+
+class TestPictureSize : public AfterStartPreview {
+public:
+ void checkOnePicture(int w, int h) {
+ const float rate = 0.5; // byte per pixel limit
+ int pixels = w * h;
+
+ CameraParameters param(c->getParameters());
+ param.setPictureSize(w, h);
+ c->setParameters(param.flatten());
+
+ cc->clearStat();
+ ASSERT(c->takePicture() == NO_ERROR);
+ cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1);
+ cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2);
+ cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1);
+ cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT,
+ int(pixels * rate));
+ cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0);
+ cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0);
+ usleep(100000); // 100ms
+ }
+
+ void run() {
+ checkOnePicture(2048, 1536);
+ checkOnePicture(1600, 1200);
+ checkOnePicture(1024, 768);
+ }
+};
+
+class TestPreviewCallbackFlag : public AfterConnect {
+public:
+ void run() {
+ sp<MSurface> surface = new MSurface();
+ ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
+
+ // Try all flag combinations.
+ for (int v = 0; v < 8; v++) {
+ cc->clearStat();
+ c->setPreviewCallbackFlag(v);
+ ASSERT(c->previewEnabled() == false);
+ ASSERT(c->startPreview() == NO_ERROR);
+ ASSERT(c->previewEnabled() == true);
+ sleep(2);
+ c->stopPreview();
+ if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) {
+ cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0);
+ } else {
+ if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) {
+ cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10);
+ } else {
+ cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1);
+ }
+ }
+ }
+ }
+};
+
+class TestRecording : public AfterConnect {
+public:
+ void run() {
+ ASSERT(c->recordingEnabled() == false);
+ sp<MSurface> surface = new MSurface();
+ ASSERT(c->setPreviewDisplay(surface) == NO_ERROR);
+ c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ cc->setReleaser(c.get());
+ c->startRecording();
+ ASSERT(c->recordingEnabled() == true);
+ sleep(2);
+ c->stopRecording();
+ cc->setReleaser(NULL);
+ cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10);
+ }
+};
+
+class TestPreviewSize : public AfterStartPreview {
+public:
+ void checkOnePicture(int w, int h) {
+ int size = w*h*3/2; // should read from parameters
+
+ c->stopPreview();
+
+ CameraParameters param(c->getParameters());
+ param.setPreviewSize(w, h);
+ c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ c->setParameters(param.flatten());
+
+ c->startPreview();
+
+ cc->clearStat();
+ cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1);
+ cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size);
+ }
+
+ void run() {
+ checkOnePicture(480, 320);
+ checkOnePicture(352, 288);
+ checkOnePicture(176, 144);
+ }
+};
+
+void runHolderService() {
+ defaultServiceManager()->addService(
+ String16("CameraServiceTest.Holder"), new HolderService());
+ ProcessState::self()->startThreadPool();
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 1) {
+ runFunction(argv[1]);
+ return 0;
+ }
+ INFO("CameraServiceTest start");
+ gExecutable = argv[0];
+ runHolderService();
+
+ testConnect(); flushCommands();
+ testAllowConnectOnceOnly(); flushCommands();
+ testReconnect(); flushCommands();
+ testLockUnlock(); flushCommands();
+ testReconnectFromAnotherProcess(); flushCommands();
+
+ RUN(TestSetPreviewDisplay);
+ RUN(TestStartPreview);
+ RUN(TestStartPreviewWithoutDisplay);
+ RUN(TestAutoFocus);
+ RUN(TestStopPreview);
+ RUN(TestTakePicture);
+ RUN(TestTakeMultiplePictures);
+ RUN(TestGetParameters);
+ RUN(TestPictureSize);
+ RUN(TestPreviewCallbackFlag);
+ RUN(TestRecording);
+ RUN(TestPreviewSize);
+}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 88463b0..68787cd 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2149,6 +2149,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void removeWindowInnerLocked(Session session, WindowState win) {
mKeyWaiter.finishedKey(session, win.mClient, true,
KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releaseMotionTarget(win);
mKeyWaiter.releasePendingPointerLocked(win.mSession);
mKeyWaiter.releasePendingTrackballLocked(win.mSession);
@@ -6120,6 +6121,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void releaseMotionTarget(WindowState win) {
+ if (mMotionTarget == win) {
+ mMotionTarget = null;
+ }
+ }
+
MotionEvent finishedKey(Session session, IWindow client, boolean force,
int returnWhat) {
if (DEBUG_INPUT) Slog.v(
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 0c11940..a388311 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -52,6 +52,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentReceiver;
@@ -1176,7 +1177,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
d.setCancelable(false);
d.setTitle("System UIDs Inconsistent");
d.setMessage("UIDs on the system are inconsistent, you need to wipe your data partition or your device will be unstable.");
- d.setButton("I'm Feeling Lucky",
+ d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
mUidAlert = d;
d.show();
@@ -5879,10 +5880,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
if (pkgs != null) {
for (String pkg : pkgs) {
- if (forceStopPackageLocked(pkg, -1, false, false, false)) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
+ synchronized (ActivityManagerService.this) {
+ if (forceStopPackageLocked(pkg, -1, false, false, false)) {
+ setResultCode(Activity.RESULT_OK);
+ return;
+ }
+ }
}
}
}
diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
index 8e9818d..9fb48b3 100644
--- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
+++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -49,7 +50,7 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog {
text.append(" is waiting for the debugger to attach.");
setMessage(text.toString());
- setButton("Force Close", mHandler.obtainMessage(1, app));
+ setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app));
setTitle("Waiting For Debugger");
getWindow().setTitle("Waiting For Debugger: " + app.info.processName);
}
diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java
index 2e25474..b19bb5c 100644
--- a/services/java/com/android/server/am/FactoryErrorDialog.java
+++ b/services/java/com/android/server/am/FactoryErrorDialog.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
@@ -26,7 +27,8 @@ class FactoryErrorDialog extends BaseErrorDialog {
setCancelable(false);
setTitle(context.getText(com.android.internal.R.string.factorytest_failed));
setMessage(msg);
- setButton(context.getText(com.android.internal.R.string.factorytest_reboot),
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getText(com.android.internal.R.string.factorytest_reboot),
mHandler.obtainMessage(0));
getWindow().setTitle("Factory Error");
}
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
new file mode 100644
index 0000000..86eb78d
--- /dev/null
+++ b/services/surfaceflinger/Android.mk
@@ -0,0 +1,51 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ clz.cpp.arm \
+ DisplayHardware/DisplayHardware.cpp \
+ DisplayHardware/DisplayHardwareBase.cpp \
+ BlurFilter.cpp.arm \
+ Layer.cpp \
+ LayerBase.cpp \
+ LayerBuffer.cpp \
+ LayerBlur.cpp \
+ LayerDim.cpp \
+ MessageQueue.cpp \
+ SurfaceFlinger.cpp \
+ Tokenizer.cpp \
+ Transform.cpp
+
+LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+ifeq ($(TARGET_BOARD_PLATFORM), msm7k)
+ LOCAL_CFLAGS += -DDIM_WITH_TEXTURE
+endif
+
+# need "-lrt" on Linux simulator to pick up clock_gettime
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libpixelflinger \
+ libhardware \
+ libutils \
+ libEGL \
+ libGLESv1_CM \
+ libbinder \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, corecg graphics)
+
+LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc
+
+LOCAL_MODULE:= libsurfaceflinger
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
new file mode 100644
index 0000000..e2bcf6a
--- /dev/null
+++ b/services/surfaceflinger/Barrier.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BARRIER_H
+#define ANDROID_BARRIER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class Barrier
+{
+public:
+ inline Barrier() : state(CLOSED) { }
+ inline ~Barrier() { }
+ void open() {
+ // gcc memory barrier, this makes sure all memory writes
+ // have been issued by gcc. On an SMP system we'd need a real
+ // h/w barrier.
+ asm volatile ("":::"memory");
+ Mutex::Autolock _l(lock);
+ state = OPENED;
+ cv.broadcast();
+ }
+ void close() {
+ Mutex::Autolock _l(lock);
+ state = CLOSED;
+ }
+ void wait() const {
+ Mutex::Autolock _l(lock);
+ while (state == CLOSED) {
+ cv.wait(lock);
+ }
+ }
+private:
+ enum { OPENED, CLOSED };
+ mutable Mutex lock;
+ mutable Condition cv;
+ volatile int state;
+};
+
+}; // namespace android
+
+#endif // ANDROID_BARRIER_H
diff --git a/services/surfaceflinger/BlurFilter.cpp b/services/surfaceflinger/BlurFilter.cpp
new file mode 100644
index 0000000..1ffbd5b
--- /dev/null
+++ b/services/surfaceflinger/BlurFilter.cpp
@@ -0,0 +1,376 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <utils/Errors.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "clz.h"
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+namespace android {
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) {
+ return v;
+}
+inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) {
+ return v;
+}
+#else
+inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) {
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) {
+ return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00);
+}
+#endif
+
+const int BLUR_DITHER_BITS = 6; // dither weights stored on 6 bits
+const int BLUR_DITHER_ORDER_SHIFT= 3;
+const int BLUR_DITHER_ORDER = (1<<BLUR_DITHER_ORDER_SHIFT);
+const int BLUR_DITHER_SIZE = BLUR_DITHER_ORDER * BLUR_DITHER_ORDER;
+const int BLUR_DITHER_MASK = BLUR_DITHER_ORDER-1;
+
+static const uint8_t gDitherMatrix[BLUR_DITHER_SIZE] = {
+ 0, 32, 8, 40, 2, 34, 10, 42,
+ 48, 16, 56, 24, 50, 18, 58, 26,
+ 12, 44, 4, 36, 14, 46, 6, 38,
+ 60, 28, 52, 20, 62, 30, 54, 22,
+ 3, 35, 11, 43, 1, 33, 9, 41,
+ 51, 19, 59, 27, 49, 17, 57, 25,
+ 15, 47, 7, 39, 13, 45, 5, 37,
+ 63, 31, 55, 23, 61, 29, 53, 21
+};
+
+
+template <int FACTOR = 0>
+struct BlurColor565
+{
+ typedef uint16_t type;
+ int r, g, b;
+ inline BlurColor565() { }
+ inline BlurColor565(uint16_t v) {
+ r = v >> 11;
+ g = (v >> 5) & 0x3E;
+ b = v & 0x1F;
+ }
+ inline void clear() { r=g=b=0; }
+ inline uint16_t to(int shift, int last, int dither) const {
+ int R = r;
+ int G = g;
+ int B = b;
+ if (UNLIKELY(last)) {
+ if (FACTOR>0) {
+ int L = (R+G+B)>>1;
+ R += (((L>>1) - R) * FACTOR) >> 8;
+ G += (((L ) - G) * FACTOR) >> 8;
+ B += (((L>>1) - B) * FACTOR) >> 8;
+ }
+ R += (dither << shift) >> BLUR_DITHER_BITS;
+ G += (dither << shift) >> BLUR_DITHER_BITS;
+ B += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ R >>= shift;
+ G >>= shift;
+ B >>= shift;
+ return (R<<11) | (G<<5) | B;
+ }
+ inline BlurColor565& operator += (const BlurColor565& rhs) {
+ r += rhs.r;
+ g += rhs.g;
+ b += rhs.b;
+ return *this;
+ }
+ inline BlurColor565& operator -= (const BlurColor565& rhs) {
+ r -= rhs.r;
+ g -= rhs.g;
+ b -= rhs.b;
+ return *this;
+ }
+};
+
+template <int FACTOR = 0>
+struct BlurColor888X
+{
+ typedef uint32_t type;
+ int r, g, b;
+ inline BlurColor888X() { }
+ inline BlurColor888X(uint32_t v) {
+ v = BLUR_RGBA_TO_HOST(v);
+ r = v & 0xFF;
+ g = (v >> 8) & 0xFF;
+ b = (v >> 16) & 0xFF;
+ }
+ inline void clear() { r=g=b=0; }
+ inline uint32_t to(int shift, int last, int dither) const {
+ int R = r;
+ int G = g;
+ int B = b;
+ if (UNLIKELY(last)) {
+ if (FACTOR>0) {
+ int L = (R+G+G+B)>>2;
+ R += ((L - R) * FACTOR) >> 8;
+ G += ((L - G) * FACTOR) >> 8;
+ B += ((L - B) * FACTOR) >> 8;
+ }
+ }
+ R >>= shift;
+ G >>= shift;
+ B >>= shift;
+ return BLUR_HOST_TO_RGBA((0xFF<<24) | (B<<16) | (G<<8) | R);
+ }
+ inline BlurColor888X& operator += (const BlurColor888X& rhs) {
+ r += rhs.r;
+ g += rhs.g;
+ b += rhs.b;
+ return *this;
+ }
+ inline BlurColor888X& operator -= (const BlurColor888X& rhs) {
+ r -= rhs.r;
+ g -= rhs.g;
+ b -= rhs.b;
+ return *this;
+ }
+};
+
+struct BlurGray565
+{
+ typedef uint16_t type;
+ int l;
+ inline BlurGray565() { }
+ inline BlurGray565(uint16_t v) {
+ int r = v >> 11;
+ int g = (v >> 5) & 0x3F;
+ int b = v & 0x1F;
+ l = (r + g + b + 1)>>1;
+ }
+ inline void clear() { l=0; }
+ inline uint16_t to(int shift, int last, int dither) const {
+ int L = l;
+ if (UNLIKELY(last)) {
+ L += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ L >>= shift;
+ return ((L>>1)<<11) | (L<<5) | (L>>1);
+ }
+ inline BlurGray565& operator += (const BlurGray565& rhs) {
+ l += rhs.l;
+ return *this;
+ }
+ inline BlurGray565& operator -= (const BlurGray565& rhs) {
+ l -= rhs.l;
+ return *this;
+ }
+};
+
+struct BlurGray8888
+{
+ typedef uint32_t type;
+ int l, a;
+ inline BlurGray8888() { }
+ inline BlurGray8888(uint32_t v) {
+ v = BLUR_RGBA_TO_HOST(v);
+ int r = v & 0xFF;
+ int g = (v >> 8) & 0xFF;
+ int b = (v >> 16) & 0xFF;
+ a = v >> 24;
+ l = r + g + g + b;
+ }
+ inline void clear() { l=a=0; }
+ inline uint32_t to(int shift, int last, int dither) const {
+ int L = l;
+ int A = a;
+ if (UNLIKELY(last)) {
+ L += (dither << (shift+2)) >> BLUR_DITHER_BITS;
+ A += (dither << shift) >> BLUR_DITHER_BITS;
+ }
+ L >>= (shift+2);
+ A >>= shift;
+ return BLUR_HOST_TO_RGBA((A<<24) | (L<<16) | (L<<8) | L);
+ }
+ inline BlurGray8888& operator += (const BlurGray8888& rhs) {
+ l += rhs.l;
+ a += rhs.a;
+ return *this;
+ }
+ inline BlurGray8888& operator -= (const BlurGray8888& rhs) {
+ l -= rhs.l;
+ a -= rhs.a;
+ return *this;
+ }
+};
+
+
+template<typename PIXEL>
+static status_t blurFilter(
+ GGLSurface const* dst,
+ GGLSurface const* src,
+ int kernelSizeUser,
+ int repeat)
+{
+ typedef typename PIXEL::type TYPE;
+
+ const int shift = 31 - clz(kernelSizeUser);
+ const int areaShift = shift*2;
+ const int kernelSize = 1<<shift;
+ const int kernelHalfSize = kernelSize/2;
+ const int mask = kernelSize-1;
+ const int w = src->width;
+ const int h = src->height;
+ const uint8_t* ditherMatrix = gDitherMatrix;
+
+ // we need a temporary buffer to store one line of blurred columns
+ // as well as kernelSize lines of source pixels organized as a ring buffer.
+ void* const temporary_buffer = malloc(
+ (w + kernelSize) * sizeof(PIXEL) +
+ (src->stride * kernelSize) * sizeof(TYPE));
+ if (!temporary_buffer)
+ return NO_MEMORY;
+
+ PIXEL* const sums = (PIXEL*)temporary_buffer;
+ TYPE* const scratch = (TYPE*)(sums + w + kernelSize);
+
+ // Apply the blur 'repeat' times, this is used to approximate
+ // gaussian blurs. 3 times gives good results.
+ for (int k=0 ; k<repeat ; k++) {
+
+ // Clear the columns sums for this round
+ memset(sums, 0, (w + kernelSize) * sizeof(PIXEL));
+ TYPE* head;
+ TYPE pixel;
+ PIXEL current;
+
+ // Since we're going to override the source data we need
+ // to copy it in a temporary buffer. Only kernelSize lines are
+ // required. But since we start in the center of the kernel,
+ // we only copy half of the data, and fill the rest with zeros
+ // (assuming black/transparent pixels).
+ memcpy( scratch + src->stride*kernelHalfSize,
+ src->data,
+ src->stride*kernelHalfSize*sizeof(TYPE));
+
+ // sum half of each column, because we assume the first half is
+ // zeros (black/transparent).
+ for (int y=0 ; y<kernelHalfSize ; y++) {
+ head = (TYPE*)src->data + y*src->stride;
+ for (int x=0 ; x<w ; x++)
+ sums[x] += PIXEL( *head++ );
+ }
+
+ for (int y=0 ; y<h ; y++) {
+ TYPE* fb = (TYPE*)dst->data + y*dst->stride;
+
+ // compute the dither matrix line
+ uint8_t const * ditherY = ditherMatrix
+ + (y & BLUR_DITHER_MASK)*BLUR_DITHER_ORDER;
+
+ // Horizontal blur pass on the columns sums
+ int count, dither, x=0;
+ PIXEL const * out= sums;
+ PIXEL const * in = sums;
+ current.clear();
+
+ count = kernelHalfSize;
+ do {
+ current += *in;
+ in++;
+ } while (--count);
+
+ count = kernelHalfSize;
+ do {
+ current += *in;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ in++;
+ } while (--count);
+
+ count = w-kernelSize;
+ do {
+ current += *in;
+ current -= *out;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ in++, out++;
+ } while (--count);
+
+ count = kernelHalfSize;
+ do {
+ current -= *out;
+ dither = *(ditherY + ((x++)&BLUR_DITHER_MASK));
+ *fb++ = current.to(areaShift, k==repeat-1, dither);
+ out++;
+ } while (--count);
+
+ // vertical blur pass, subtract the oldest line from each columns
+ // and add a new line. Subtract or add zeros at the top
+ // and bottom edges.
+ TYPE* const tail = scratch + (y & mask) * src->stride;
+ if (y >= kernelHalfSize) {
+ for (int x=0 ; x<w ; x++)
+ sums[x] -= PIXEL( tail[x] );
+ }
+ if (y < h-kernelSize) {
+ memcpy( tail,
+ (TYPE*)src->data + (y+kernelHalfSize)*src->stride,
+ src->stride*sizeof(TYPE));
+ for (int x=0 ; x<w ; x++)
+ sums[x] += PIXEL( tail[x] );
+ }
+ }
+
+ // The subsequent passes are always done in-place.
+ src = dst;
+ }
+
+ free(temporary_buffer);
+
+ return NO_ERROR;
+}
+
+template status_t blurFilter< BlurColor565<0x80> >(
+ GGLSurface const* dst,
+ GGLSurface const* src,
+ int kernelSizeUser,
+ int repeat);
+
+status_t blurFilter(
+ GGLSurface const* image,
+ int kernelSizeUser,
+ int repeat)
+{
+ status_t err = BAD_VALUE;
+ if (image->format == GGL_PIXEL_FORMAT_RGB_565) {
+ err = blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat);
+ } else if (image->format == GGL_PIXEL_FORMAT_RGBX_8888) {
+ err = blurFilter< BlurColor888X<0x80> >(image, image, kernelSizeUser, repeat);
+ }
+ return err;
+}
+
+} // namespace android
+
+//err = blur< BlurColor565<0x80> >(dst, src, kernelSizeUser, repeat);
+//err = blur<BlurGray565>(dst, src, kernelSizeUser, repeat);
+//err = blur<BlurGray8888>(dst, src, kernelSizeUser, repeat);
diff --git a/services/surfaceflinger/BlurFilter.h b/services/surfaceflinger/BlurFilter.h
new file mode 100644
index 0000000..294db43
--- /dev/null
+++ b/services/surfaceflinger/BlurFilter.h
@@ -0,0 +1,35 @@
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_BLUR_FILTER_H
+#define ANDROID_BLUR_FILTER_H
+
+#include <stdint.h>
+#include <utils/Errors.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+namespace android {
+
+status_t blurFilter(
+ GGLSurface const* image,
+ int kernelSizeUser,
+ int repeat);
+
+} // namespace android
+
+#endif // ANDROID_BLUR_FILTER_H
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
new file mode 100644
index 0000000..ea68352
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <cutils/properties.h>
+
+#include <utils/RefBase.h>
+#include <utils/Log.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+#include <GLES/gl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "DisplayHardware/DisplayHardware.h"
+
+#include <hardware/copybit.h>
+#include <hardware/overlay.h>
+#include <hardware/gralloc.h>
+
+using namespace android;
+
+
+static __attribute__((noinline))
+void checkGLErrors()
+{
+ do {
+ // there could be more than one error flag
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR)
+ break;
+ LOGE("GL error 0x%04x", int(error));
+ } while(true);
+}
+
+static __attribute__((noinline))
+void checkEGLErrors(const char* token)
+{
+ EGLint error = eglGetError();
+ if (error && error != EGL_SUCCESS) {
+ LOGE("%s: EGL error 0x%04x (%s)",
+ token, int(error), EGLUtils::strerror(error));
+ }
+}
+
+/*
+ * Initialize the display to the specified values.
+ *
+ */
+
+DisplayHardware::DisplayHardware(
+ const sp<SurfaceFlinger>& flinger,
+ uint32_t dpy)
+ : DisplayHardwareBase(flinger, dpy)
+{
+ init(dpy);
+}
+
+DisplayHardware::~DisplayHardware()
+{
+ fini();
+}
+
+float DisplayHardware::getDpiX() const { return mDpiX; }
+float DisplayHardware::getDpiY() const { return mDpiY; }
+float DisplayHardware::getDensity() const { return mDensity; }
+float DisplayHardware::getRefreshRate() const { return mRefreshRate; }
+int DisplayHardware::getWidth() const { return mWidth; }
+int DisplayHardware::getHeight() const { return mHeight; }
+PixelFormat DisplayHardware::getFormat() const { return mFormat; }
+uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; }
+uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; }
+
+void DisplayHardware::init(uint32_t dpy)
+{
+ mNativeWindow = new FramebufferNativeWindow();
+ framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+
+ mOverlayEngine = NULL;
+ hw_module_t const* module;
+ if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
+ overlay_control_open(module, &mOverlayEngine);
+ }
+
+ // initialize EGL
+ EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE, 0,
+ EGL_NONE
+ };
+
+ // debug: disable h/w rendering
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get("debug.sf.hw", property, NULL) > 0) {
+ if (atoi(property) == 0) {
+ LOGW("H/W composition disabled");
+ attribs[2] = EGL_CONFIG_CAVEAT;
+ attribs[3] = EGL_SLOW_CONFIG;
+ }
+ }
+
+ EGLint w, h, dummy;
+ EGLint numConfigs=0;
+ EGLSurface surface;
+ EGLContext context;
+ mFlags = CACHED_BUFFERS;
+
+ // TODO: all the extensions below should be queried through
+ // eglGetProcAddress().
+
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(display, NULL, NULL);
+ eglGetConfigs(display, NULL, 0, &numConfigs);
+
+ EGLConfig config;
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ display, attribs, mNativeWindow.get(), &config);
+ LOGE_IF(err, "couldn't find an EGLConfig matching the screen format");
+
+ EGLint r,g,b,a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+
+ /*
+ * Gather EGL extensions
+ */
+
+ const char* const egl_extensions = eglQueryString(
+ display, EGL_EXTENSIONS);
+
+ LOGI("EGL informations:");
+ LOGI("# of configs : %d", numConfigs);
+ LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ LOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ LOGI("extensions: %s", egl_extensions);
+ LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
+ LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+
+
+ if (mNativeWindow->isUpdateOnDemand()) {
+ mFlags |= PARTIAL_UPDATES;
+ }
+
+ if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) {
+ if (dummy == EGL_SLOW_CONFIG)
+ mFlags |= SLOW_CONFIG;
+ }
+
+ /*
+ * Create our main surface
+ */
+
+ surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
+
+ if (mFlags & PARTIAL_UPDATES) {
+ // if we have partial updates, we definitely don't need to
+ // preserve the backbuffer, which may be costly.
+ eglSurfaceAttrib(display, surface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
+ }
+
+ if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
+ if (dummy == EGL_BUFFER_PRESERVED) {
+ mFlags |= BUFFER_PRESERVED;
+ }
+ }
+
+ eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
+
+#ifdef EGL_ANDROID_swap_rectangle
+ if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) {
+ if (eglSetSwapRectangleANDROID(display, surface,
+ 0, 0, mWidth, mHeight) == EGL_TRUE) {
+ // This could fail if this extension is not supported by this
+ // specific surface (of config)
+ mFlags |= SWAP_RECTANGLE;
+ }
+ }
+ // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
+ // choose PARTIAL_UPDATES, which should be more efficient
+ if (mFlags & PARTIAL_UPDATES)
+ mFlags &= ~SWAP_RECTANGLE;
+#endif
+
+
+ LOGI("flags : %08x", mFlags);
+
+ mDpiX = mNativeWindow->xdpi;
+ mDpiY = mNativeWindow->ydpi;
+ mRefreshRate = fbDev->fps;
+
+ /* Read density from build-specific ro.sf.lcd_density property
+ * except if it is overridden by qemu.sf.lcd_density.
+ */
+ if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
+ if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
+ LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
+ strcpy(property, "160");
+ }
+ } else {
+ /* for the emulator case, reset the dpi values too */
+ mDpiX = mDpiY = atoi(property);
+ }
+ mDensity = atoi(property) * (1.0f/160.0f);
+
+
+ /*
+ * Create our OpenGL ES context
+ */
+
+ context = eglCreateContext(display, config, NULL, NULL);
+
+ /*
+ * Gather OpenGL ES extensions
+ */
+
+ eglMakeCurrent(display, surface, surface, context);
+ const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS);
+ const char* const gl_renderer = (const char*)glGetString(GL_RENDERER);
+ LOGI("OpenGL informations:");
+ LOGI("vendor : %s", glGetString(GL_VENDOR));
+ LOGI("renderer : %s", gl_renderer);
+ LOGI("version : %s", glGetString(GL_VERSION));
+ LOGI("extensions: %s", gl_extensions);
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);
+ LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
+ LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
+
+#if 0
+ // for drivers that don't have proper support for flushing cached buffers
+ // on gralloc unlock, uncomment this block and test for the specific
+ // renderer substring
+ if (strstr(gl_renderer, "<some vendor string>")) {
+ LOGD("Assuming uncached graphics buffers.");
+ mFlags &= ~CACHED_BUFFERS;
+ }
+#endif
+
+ if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) {
+ mFlags |= NPOT_EXTENSION;
+ }
+ if (strstr(gl_extensions, "GL_OES_draw_texture")) {
+ mFlags |= DRAW_TEXTURE_EXTENSION;
+ }
+#ifdef EGL_ANDROID_image_native_buffer
+ if (strstr( gl_extensions, "GL_OES_EGL_image") &&
+ (strstr(egl_extensions, "EGL_KHR_image_base") ||
+ strstr(egl_extensions, "EGL_KHR_image")) &&
+ strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) {
+ mFlags |= DIRECT_TEXTURE;
+ }
+#else
+#warning "EGL_ANDROID_image_native_buffer not supported"
+#endif
+
+
+ // Unbind the context from this thread
+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ mDisplay = display;
+ mConfig = config;
+ mSurface = surface;
+ mContext = context;
+ mFormat = fbDev->format;
+ mPageFlipCount = 0;
+}
+
+/*
+ * Clean up. Throw out our local state.
+ *
+ * (It's entirely possible we'll never get here, since this is meant
+ * for real hardware, which doesn't restart.)
+ */
+
+void DisplayHardware::fini()
+{
+ eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mDisplay);
+ overlay_control_close(mOverlayEngine);
+}
+
+void DisplayHardware::releaseScreen() const
+{
+ DisplayHardwareBase::releaseScreen();
+}
+
+void DisplayHardware::acquireScreen() const
+{
+ DisplayHardwareBase::acquireScreen();
+}
+
+uint32_t DisplayHardware::getPageFlipCount() const {
+ return mPageFlipCount;
+}
+
+status_t DisplayHardware::compositionComplete() const {
+ return mNativeWindow->compositionComplete();
+}
+
+void DisplayHardware::flip(const Region& dirty) const
+{
+ checkGLErrors();
+
+ EGLDisplay dpy = mDisplay;
+ EGLSurface surface = mSurface;
+
+#ifdef EGL_ANDROID_swap_rectangle
+ if (mFlags & SWAP_RECTANGLE) {
+ const Region newDirty(dirty.intersect(bounds()));
+ const Rect b(newDirty.getBounds());
+ eglSetSwapRectangleANDROID(dpy, surface,
+ b.left, b.top, b.width(), b.height());
+ }
+#endif
+
+ if (mFlags & PARTIAL_UPDATES) {
+ mNativeWindow->setUpdateRectangle(dirty.getBounds());
+ }
+
+ mPageFlipCount++;
+ eglSwapBuffers(dpy, surface);
+ checkEGLErrors("eglSwapBuffers");
+
+ // for debugging
+ //glClearColor(1,0,0,0);
+ //glClear(GL_COLOR_BUFFER_BIT);
+}
+
+uint32_t DisplayHardware::getFlags() const
+{
+ return mFlags;
+}
+
+void DisplayHardware::makeCurrent() const
+{
+ eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
+}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
new file mode 100644
index 0000000..df046af
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DISPLAY_HARDWARE_H
+#define ANDROID_DISPLAY_HARDWARE_H
+
+#include <stdlib.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Region.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "DisplayHardware/DisplayHardwareBase.h"
+
+struct overlay_control_device_t;
+struct framebuffer_device_t;
+struct copybit_image_t;
+
+namespace android {
+
+class FramebufferNativeWindow;
+
+class DisplayHardware : public DisplayHardwareBase
+{
+public:
+ enum {
+ DIRECT_TEXTURE = 0x00000002,
+ COPY_BITS_EXTENSION = 0x00000008,
+ NPOT_EXTENSION = 0x00000100,
+ DRAW_TEXTURE_EXTENSION = 0x00000200,
+ BUFFER_PRESERVED = 0x00010000,
+ PARTIAL_UPDATES = 0x00020000, // video driver feature
+ SLOW_CONFIG = 0x00040000, // software
+ SWAP_RECTANGLE = 0x00080000,
+ CACHED_BUFFERS = 0x00100000
+ };
+
+ DisplayHardware(
+ const sp<SurfaceFlinger>& flinger,
+ uint32_t displayIndex);
+
+ ~DisplayHardware();
+
+ void releaseScreen() const;
+ void acquireScreen() const;
+
+ // Flip the front and back buffers if the back buffer is "dirty". Might
+ // be instantaneous, might involve copying the frame buffer around.
+ void flip(const Region& dirty) const;
+
+ float getDpiX() const;
+ float getDpiY() const;
+ float getRefreshRate() const;
+ float getDensity() const;
+ int getWidth() const;
+ int getHeight() const;
+ PixelFormat getFormat() const;
+ uint32_t getFlags() const;
+ void makeCurrent() const;
+ uint32_t getMaxTextureSize() const;
+ uint32_t getMaxViewportDims() const;
+
+ uint32_t getPageFlipCount() const;
+ EGLDisplay getEGLDisplay() const { return mDisplay; }
+ overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+ status_t compositionComplete() const;
+
+ Rect bounds() const {
+ return Rect(mWidth, mHeight);
+ }
+
+private:
+ void init(uint32_t displayIndex) __attribute__((noinline));
+ void fini() __attribute__((noinline));
+
+ EGLDisplay mDisplay;
+ EGLSurface mSurface;
+ EGLContext mContext;
+ EGLConfig mConfig;
+ float mDpiX;
+ float mDpiY;
+ float mRefreshRate;
+ float mDensity;
+ int mWidth;
+ int mHeight;
+ PixelFormat mFormat;
+ uint32_t mFlags;
+ mutable uint32_t mPageFlipCount;
+ GLint mMaxViewportDims;
+ GLint mMaxTextureSize;
+
+ sp<FramebufferNativeWindow> mNativeWindow;
+ overlay_control_device_t* mOverlayEngine;
+};
+
+}; // namespace android
+
+#endif // ANDROID_DISPLAY_HARDWARE_H
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
new file mode 100644
index 0000000..1d09f84
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#include <linux/unistd.h>
+
+#include <utils/Log.h>
+
+#include "DisplayHardware/DisplayHardwareBase.h"
+#include "SurfaceFlinger.h"
+
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+// ----------------------------------------------------------------------------
+namespace android {
+
+static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
+static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
+static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
+static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";
+
+// This dir exists if the framebuffer console is present, either built into
+// the kernel or loaded as a module.
+static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";
+
+// ----------------------------------------------------------------------------
+
+DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase(
+ const sp<SurfaceFlinger>& flinger)
+ : Thread(false), mFlinger(flinger) {
+}
+
+DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() {
+}
+
+// ----------------------------------------------------------------------------
+
+DisplayHardwareBase::DisplayEventThread::DisplayEventThread(
+ const sp<SurfaceFlinger>& flinger)
+ : DisplayEventThreadBase(flinger)
+{
+}
+
+DisplayHardwareBase::DisplayEventThread::~DisplayEventThread()
+{
+}
+
+bool DisplayHardwareBase::DisplayEventThread::threadLoop()
+{
+ int err = 0;
+ char buf;
+ int fd;
+
+ fd = open(kSleepFileName, O_RDONLY, 0);
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ close(fd);
+ LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));
+ if (err >= 0) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ LOGD("About to give-up screen, flinger = %p", flinger.get());
+ if (flinger != 0) {
+ mBarrier.close();
+ flinger->screenReleased(0);
+ mBarrier.wait();
+ }
+ }
+ fd = open(kWakeFileName, O_RDONLY, 0);
+ do {
+ err = read(fd, &buf, 1);
+ } while (err < 0 && errno == EINTR);
+ close(fd);
+ LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));
+ if (err >= 0) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ LOGD("Screen about to return, flinger = %p", flinger.get());
+ if (flinger != 0)
+ flinger->screenAcquired(0);
+ }
+ return true;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const
+{
+ mBarrier.open();
+ return NO_ERROR;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
+{
+ if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
+ if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
+ LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
+ return NO_INIT;
+ }
+ kSleepFileName = kOldSleepFileName;
+ kWakeFileName = kOldWakeFileName;
+ }
+ return NO_ERROR;
+}
+
+status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
+{
+ return (((access(kSleepFileName, R_OK) == 0 &&
+ access(kWakeFileName, R_OK) == 0) ||
+ (access(kOldSleepFileName, R_OK) == 0 &&
+ access(kOldWakeFileName, R_OK) == 0)) &&
+ access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
+}
+
+// ----------------------------------------------------------------------------
+
+pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0;
+
+DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread(
+ const sp<SurfaceFlinger>& flinger)
+ : DisplayEventThreadBase(flinger), consoleFd(-1)
+{
+ sSignalCatcherPid = 0;
+
+ // create a new console
+ char const * const ttydev = "/dev/tty0";
+ int fd = open(ttydev, O_RDWR | O_SYNC);
+ if (fd<0) {
+ LOGE("Can't open %s", ttydev);
+ this->consoleFd = -errno;
+ return;
+ }
+
+ // to make sure that we are in text mode
+ int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT);
+ if (res<0) {
+ LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)",
+ fd, res, strerror(errno));
+ }
+
+ // get the current console
+ struct vt_stat vs;
+ res = ioctl(fd, VT_GETSTATE, &vs);
+ if (res<0) {
+ LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)",
+ fd, res, strerror(errno));
+ this->consoleFd = -errno;
+ return;
+ }
+
+ // switch to console 7 (which is what X normaly uses)
+ int vtnum = 7;
+ do {
+ res = ioctl(fd, VT_ACTIVATE, (void*)vtnum);
+ } while(res < 0 && errno == EINTR);
+ if (res<0) {
+ LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d",
+ fd, errno, strerror(errno), vtnum);
+ this->consoleFd = -errno;
+ return;
+ }
+
+ do {
+ res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum);
+ } while(res < 0 && errno == EINTR);
+ if (res<0) {
+ LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d",
+ fd, res, errno, strerror(errno), vtnum);
+ this->consoleFd = -errno;
+ return;
+ }
+
+ // open the new console
+ close(fd);
+ fd = open(ttydev, O_RDWR | O_SYNC);
+ if (fd<0) {
+ LOGE("Can't open new console %s", ttydev);
+ this->consoleFd = -errno;
+ return;
+ }
+
+ /* disable console line buffer, echo, ... */
+ struct termios ttyarg;
+ ioctl(fd, TCGETS , &ttyarg);
+ ttyarg.c_iflag = 0;
+ ttyarg.c_lflag = 0;
+ ioctl(fd, TCSETS , &ttyarg);
+
+ // set up signals so we're notified when the console changes
+ // we can't use SIGUSR1 because it's used by the java-vm
+ vm.mode = VT_PROCESS;
+ vm.waitv = 0;
+ vm.relsig = SIGUSR2;
+ vm.acqsig = SIGUNUSED;
+ vm.frsig = 0;
+
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = sigHandler;
+ act.sa_flags = 0;
+ sigaction(vm.relsig, &act, NULL);
+
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = sigHandler;
+ act.sa_flags = 0;
+ sigaction(vm.acqsig, &act, NULL);
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, vm.relsig);
+ sigaddset(&mask, vm.acqsig);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ // switch to graphic mode
+ res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS);
+ LOGW_IF(res<0,
+ "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res);
+
+ this->prev_vt_num = vs.v_active;
+ this->vt_num = vtnum;
+ this->consoleFd = fd;
+}
+
+DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread()
+{
+ if (this->consoleFd >= 0) {
+ int fd = this->consoleFd;
+ int prev_vt_num = this->prev_vt_num;
+ int res;
+ ioctl(fd, KDSETMODE, (void*)KD_TEXT);
+ do {
+ res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num);
+ } while(res < 0 && errno == EINTR);
+ do {
+ res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num);
+ } while(res < 0 && errno == EINTR);
+ close(fd);
+ char const * const ttydev = "/dev/tty0";
+ fd = open(ttydev, O_RDWR | O_SYNC);
+ ioctl(fd, VT_DISALLOCATE, 0);
+ close(fd);
+ }
+}
+
+status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun()
+{
+ if (this->consoleFd >= 0) {
+ sSignalCatcherPid = gettid();
+
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, vm.relsig);
+ sigaddset(&mask, vm.acqsig);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ int res = ioctl(this->consoleFd, VT_SETMODE, &vm);
+ if (res<0) {
+ LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)",
+ this->consoleFd, errno, strerror(errno));
+ }
+ return NO_ERROR;
+ }
+ return this->consoleFd;
+}
+
+void DisplayHardwareBase::ConsoleManagerThread::requestExit()
+{
+ Thread::requestExit();
+ if (sSignalCatcherPid != 0) {
+ // wake the thread up
+ kill(sSignalCatcherPid, SIGINT);
+ // wait for it...
+ }
+}
+
+void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig)
+{
+ // resend the signal to our signal catcher thread
+ LOGW("received signal %d in thread %d, resending to %d",
+ sig, gettid(), sSignalCatcherPid);
+
+ // we absolutely need the delays below because without them
+ // our main thread never gets a chance to handle the signal.
+ usleep(10000);
+ kill(sSignalCatcherPid, sig);
+ usleep(10000);
+}
+
+status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const
+{
+ int fd = this->consoleFd;
+ int err = ioctl(fd, VT_RELDISP, (void*)1);
+ LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)",
+ fd, errno, strerror(errno));
+ return (err<0) ? (-errno) : status_t(NO_ERROR);
+}
+
+bool DisplayHardwareBase::ConsoleManagerThread::threadLoop()
+{
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, vm.relsig);
+ sigaddset(&mask, vm.acqsig);
+
+ int sig = 0;
+ sigwait(&mask, &sig);
+
+ if (sig == vm.relsig) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ //LOGD("About to give-up screen, flinger = %p", flinger.get());
+ if (flinger != 0)
+ flinger->screenReleased(0);
+ } else if (sig == vm.acqsig) {
+ sp<SurfaceFlinger> flinger = mFlinger.promote();
+ //LOGD("Screen about to return, flinger = %p", flinger.get());
+ if (flinger != 0)
+ flinger->screenAcquired(0);
+ }
+
+ return true;
+}
+
+status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const
+{
+ return consoleFd >= 0 ? NO_ERROR : NO_INIT;
+}
+
+// ----------------------------------------------------------------------------
+
+DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
+ uint32_t displayIndex)
+ : mCanDraw(true)
+{
+ mDisplayEventThread = new DisplayEventThread(flinger);
+ if (mDisplayEventThread->initCheck() != NO_ERROR) {
+ // fall-back on the console
+ mDisplayEventThread = new ConsoleManagerThread(flinger);
+ }
+}
+
+DisplayHardwareBase::~DisplayHardwareBase()
+{
+ // request exit
+ mDisplayEventThread->requestExitAndWait();
+}
+
+
+bool DisplayHardwareBase::canDraw() const
+{
+ return mCanDraw;
+}
+
+void DisplayHardwareBase::releaseScreen() const
+{
+ status_t err = mDisplayEventThread->releaseScreen();
+ if (err >= 0) {
+ //LOGD("screen given-up");
+ mCanDraw = false;
+ }
+}
+
+void DisplayHardwareBase::acquireScreen() const
+{
+ status_t err = mDisplayEventThread->acquireScreen();
+ if (err >= 0) {
+ //LOGD("screen returned");
+ mCanDraw = true;
+ }
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
new file mode 100644
index 0000000..8369bb8
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DISPLAY_HARDWARE_BASE_H
+#define ANDROID_DISPLAY_HARDWARE_BASE_H
+
+#include <stdint.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include "Barrier.h"
+
+namespace android {
+
+class SurfaceFlinger;
+
+class DisplayHardwareBase
+{
+public:
+ DisplayHardwareBase(
+ const sp<SurfaceFlinger>& flinger,
+ uint32_t displayIndex);
+
+ ~DisplayHardwareBase();
+
+ // console managment
+ void releaseScreen() const;
+ void acquireScreen() const;
+ bool canDraw() const;
+
+private:
+ class DisplayEventThreadBase : public Thread {
+ protected:
+ wp<SurfaceFlinger> mFlinger;
+ public:
+ DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger);
+ virtual ~DisplayEventThreadBase();
+ virtual void onFirstRef() {
+ run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
+ }
+ virtual status_t acquireScreen() const { return NO_ERROR; };
+ virtual status_t releaseScreen() const { return NO_ERROR; };
+ virtual status_t initCheck() const = 0;
+ };
+
+ class DisplayEventThread : public DisplayEventThreadBase
+ {
+ mutable Barrier mBarrier;
+ public:
+ DisplayEventThread(const sp<SurfaceFlinger>& flinger);
+ virtual ~DisplayEventThread();
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual status_t releaseScreen() const;
+ virtual status_t initCheck() const;
+ };
+
+ class ConsoleManagerThread : public DisplayEventThreadBase
+ {
+ int consoleFd;
+ int vt_num;
+ int prev_vt_num;
+ vt_mode vm;
+ static void sigHandler(int sig);
+ static pid_t sSignalCatcherPid;
+ public:
+ ConsoleManagerThread(const sp<SurfaceFlinger>& flinger);
+ virtual ~ConsoleManagerThread();
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void requestExit();
+ virtual status_t releaseScreen() const;
+ virtual status_t initCheck() const;
+ };
+
+ sp<DisplayEventThreadBase> mDisplayEventThread;
+ mutable int mCanDraw;
+};
+
+}; // namespace android
+
+#endif // ANDROID_DISPLAY_HARDWARE_BASE_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
new file mode 100644
index 0000000..ce7e9aa
--- /dev/null
+++ b/services/surfaceflinger/Layer.cpp
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/properties.h>
+#include <cutils/native_handle.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/StopWatch.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+
+#include <surfaceflinger/Surface.h>
+
+#include "clz.h"
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+
+#define DEBUG_RESIZE 0
+
+
+namespace android {
+
+template <typename T> inline T min(T a, T b) {
+ return a<b ? a : b;
+}
+
+// ---------------------------------------------------------------------------
+
+const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4;
+const char* const Layer::typeID = "Layer";
+
+// ---------------------------------------------------------------------------
+
+Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& c, int32_t i)
+ : LayerBaseClient(flinger, display, c, i),
+ mSecure(false),
+ mNoEGLImageForSwBuffers(false),
+ mNeedsBlending(true),
+ mNeedsDithering(false)
+{
+ // no OpenGL operation is possible here, since we might not be
+ // in the OpenGL thread.
+ mFrontBufferIndex = lcblk->getFrontBuffer();
+}
+
+Layer::~Layer()
+{
+ destroy();
+ // the actual buffers will be destroyed here
+}
+
+void Layer::destroy()
+{
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ if (mTextures[i].name != -1U) {
+ glDeleteTextures(1, &mTextures[i].name);
+ mTextures[i].name = -1U;
+ }
+ if (mTextures[i].image != EGL_NO_IMAGE_KHR) {
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ eglDestroyImageKHR(dpy, mTextures[i].image);
+ mTextures[i].image = EGL_NO_IMAGE_KHR;
+ }
+ Mutex::Autolock _l(mLock);
+ mBuffers[i].clear();
+ mWidth = mHeight = 0;
+ }
+ mSurface.clear();
+}
+
+sp<LayerBaseClient::Surface> Layer::createSurface() const
+{
+ return mSurface;
+}
+
+status_t Layer::ditch()
+{
+ // the layer is not on screen anymore. free as much resources as possible
+ mFreezeLock.clear();
+ destroy();
+ return NO_ERROR;
+}
+
+status_t Layer::setBuffers( uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags)
+{
+ // this surfaces pixel format
+ PixelFormatInfo info;
+ status_t err = getPixelFormatInfo(format, &info);
+ if (err) return err;
+
+ // the display's pixel format
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ uint32_t const maxSurfaceDims = min(
+ hw.getMaxTextureSize(), hw.getMaxViewportDims());
+
+ // never allow a surface larger than what our underlying GL implementation
+ // can handle.
+ if ((uint32_t(w)>maxSurfaceDims) || (uint32_t(h)>maxSurfaceDims)) {
+ return BAD_VALUE;
+ }
+
+ PixelFormatInfo displayInfo;
+ getPixelFormatInfo(hw.getFormat(), &displayInfo);
+ const uint32_t hwFlags = hw.getFlags();
+
+ mFormat = format;
+ mWidth = w;
+ mHeight = h;
+ mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
+ mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
+ mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS);
+
+ // we use the red index
+ int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);
+ int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);
+ mNeedsDithering = layerRedsize > displayRedSize;
+
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ mBuffers[i] = new GraphicBuffer();
+ }
+ mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
+ return NO_ERROR;
+}
+
+void Layer::reloadTexture(const Region& dirty)
+{
+ Mutex::Autolock _l(mLock);
+ sp<GraphicBuffer> buffer(getFrontBufferLocked());
+ if (buffer == NULL) {
+ // this situation can happen if we ran out of memory for instance.
+ // not much we can do. continue to use whatever texture was bound
+ // to this context.
+ return;
+ }
+
+ const int index = mFrontBufferIndex;
+
+ // create the new texture name if needed
+ if (UNLIKELY(mTextures[index].name == -1U)) {
+ mTextures[index].name = createTexture();
+ mTextures[index].width = 0;
+ mTextures[index].height = 0;
+ }
+
+#ifdef EGL_ANDROID_image_native_buffer
+ if (mFlags & DisplayHardware::DIRECT_TEXTURE) {
+ if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) {
+ if (mTextures[index].dirty) {
+ if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) {
+ // not sure what we can do here...
+ mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
+ goto slowpath;
+ }
+ }
+ } else {
+ if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width ||
+ mHybridBuffer->height != buffer->height)) {
+ mHybridBuffer.clear();
+ mHybridBuffer = new GraphicBuffer(
+ buffer->width, buffer->height, buffer->format,
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+ GraphicBuffer::USAGE_HW_TEXTURE);
+ if (initializeEglImage(
+ mHybridBuffer, &mTextures[0]) != NO_ERROR) {
+ // not sure what we can do here...
+ mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
+ mHybridBuffer.clear();
+ goto slowpath;
+ }
+ }
+
+ GGLSurface t;
+ status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
+ LOGE_IF(res, "error %d (%s) locking buffer %p",
+ res, strerror(res), buffer.get());
+ if (res == NO_ERROR) {
+ Texture* const texture(&mTextures[0]);
+
+ glBindTexture(GL_TEXTURE_2D, texture->name);
+
+ sp<GraphicBuffer> buf(mHybridBuffer);
+ void* vaddr;
+ res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr);
+ if (res == NO_ERROR) {
+ int bpp = 0;
+ switch (t.format) {
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ bpp = 2;
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ bpp = 4;
+ break;
+ default:
+ if (isSupportedYuvFormat(t.format)) {
+ // just show the Y plane of YUV buffers
+ bpp = 1;
+ break;
+ }
+ // oops, we don't handle this format!
+ LOGE("layer %p, texture=%d, using format %d, which is not "
+ "supported by the GL", this, texture->name, t.format);
+ }
+ if (bpp) {
+ const Rect bounds(dirty.getBounds());
+ size_t src_stride = t.stride;
+ size_t dst_stride = buf->stride;
+ if (src_stride == dst_stride &&
+ bounds.width() == t.width &&
+ bounds.height() == t.height)
+ {
+ memcpy(vaddr, t.data, t.height * t.stride * bpp);
+ } else {
+ GLubyte const * src = t.data +
+ (bounds.left + bounds.top * src_stride) * bpp;
+ GLubyte * dst = (GLubyte *)vaddr +
+ (bounds.left + bounds.top * dst_stride) * bpp;
+ const size_t length = bounds.width() * bpp;
+ size_t h = bounds.height();
+ src_stride *= bpp;
+ dst_stride *= bpp;
+ while (h--) {
+ memcpy(dst, src, length);
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+ }
+ buf->unlock();
+ }
+ buffer->unlock();
+ }
+ }
+ } else
+#endif
+ {
+slowpath:
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ mTextures[i].image = EGL_NO_IMAGE_KHR;
+ }
+ GGLSurface t;
+ status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN);
+ LOGE_IF(res, "error %d (%s) locking buffer %p",
+ res, strerror(res), buffer.get());
+ if (res == NO_ERROR) {
+ loadTexture(&mTextures[0], dirty, t);
+ buffer->unlock();
+ }
+ }
+}
+
+void Layer::onDraw(const Region& clip) const
+{
+ int index = mFrontBufferIndex;
+ if (mTextures[index].image == EGL_NO_IMAGE_KHR)
+ index = 0;
+ GLuint textureName = mTextures[index].name;
+ if (UNLIKELY(textureName == -1LU)) {
+ // the texture has not been created yet, this Layer has
+ // in fact never been drawn into. This happens frequently with
+ // SurfaceView because the WindowManager can't know when the client
+ // has drawn the first time.
+
+ // If there is nothing under us, we paint the screen in black, otherwise
+ // we just skip this update.
+
+ // figure out if there is something below us
+ Region under;
+ const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ);
+ const size_t count = drawingLayers.size();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer(drawingLayers[i]);
+ if (layer.get() == static_cast<LayerBase const*>(this))
+ break;
+ under.orSelf(layer->visibleRegionScreen);
+ }
+ // if not everything below us is covered, we plug the holes!
+ Region holes(clip.subtract(under));
+ if (!holes.isEmpty()) {
+ clearWithOpenGL(holes);
+ }
+ return;
+ }
+ drawWithOpenGL(clip, mTextures[index]);
+}
+
+sp<GraphicBuffer> Layer::requestBuffer(int index, int usage)
+{
+ sp<GraphicBuffer> buffer;
+
+ // this ensures our client doesn't go away while we're accessing
+ // the shared area.
+ sp<Client> ourClient(client.promote());
+ if (ourClient == 0) {
+ // oops, the client is already gone
+ return buffer;
+ }
+
+ /*
+ * This is called from the client's Surface::dequeue(). This can happen
+ * at any time, especially while we're in the middle of using the
+ * buffer 'index' as our front buffer.
+ *
+ * Make sure the buffer we're resizing is not the front buffer and has been
+ * dequeued. Once this condition is asserted, we are guaranteed that this
+ * buffer cannot become the front buffer under our feet, since we're called
+ * from Surface::dequeue()
+ */
+ status_t err = lcblk->assertReallocate(index);
+ LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err));
+ if (err != NO_ERROR) {
+ // the surface may have died
+ return buffer;
+ }
+
+ uint32_t w, h;
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ w = mWidth;
+ h = mHeight;
+ buffer = mBuffers[index];
+
+ // destroy() could have been called before we get here, we log it
+ // because it's uncommon, and the code below should handle it
+ LOGW_IF(buffer==0,
+ "mBuffers[%d] is null (mWidth=%d, mHeight=%d)",
+ index, w, h);
+
+ mBuffers[index].clear();
+ }
+
+ const uint32_t effectiveUsage = getEffectiveUsage(usage);
+ if (buffer!=0 && buffer->getStrongCount() == 1) {
+ err = buffer->reallocate(w, h, mFormat, effectiveUsage);
+ } else {
+ // here we have to reallocate a new buffer because we could have a
+ // client in our process with a reference to it (eg: status bar),
+ // and we can't release the handle under its feet.
+ buffer.clear();
+ buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);
+ err = buffer->initCheck();
+ }
+
+ if (err || buffer->handle == 0) {
+ LOGE_IF(err || buffer->handle == 0,
+ "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)",
+ this, index, w, h, strerror(-err));
+ } else {
+ LOGD_IF(DEBUG_RESIZE,
+ "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d, handle=%p",
+ this, index, w, h, buffer->handle);
+ }
+
+ if (err == NO_ERROR && buffer->handle != 0) {
+ Mutex::Autolock _l(mLock);
+ if (mWidth && mHeight) {
+ // and we have new buffer
+ mBuffers[index] = buffer;
+ // texture is now dirty...
+ mTextures[index].dirty = true;
+ } else {
+ // oops we got killed while we were allocating the buffer
+ buffer.clear();
+ }
+ }
+ return buffer;
+}
+
+uint32_t Layer::getEffectiveUsage(uint32_t usage) const
+{
+ /*
+ * buffers used for software rendering, but h/w composition
+ * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE
+ *
+ * buffers used for h/w rendering and h/w composition
+ * are allocated with HW_RENDER | HW_TEXTURE
+ *
+ * buffers used with h/w rendering and either NPOT or no egl_image_ext
+ * are allocated with SW_READ_RARELY | HW_RENDER
+ *
+ */
+
+ if (mSecure) {
+ // secure buffer, don't store it into the GPU
+ usage = GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+ } else {
+ // it's allowed to modify the usage flags here, but generally
+ // the requested flags should be honored.
+ if (mNoEGLImageForSwBuffers) {
+ if (usage & GraphicBuffer::USAGE_HW_MASK) {
+ // request EGLImage for h/w buffers only
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ }
+ } else {
+ // request EGLImage for all buffers
+ usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ }
+ }
+ return usage;
+}
+
+uint32_t Layer::doTransaction(uint32_t flags)
+{
+ const Layer::State& front(drawingState());
+ const Layer::State& temp(currentState());
+
+ if ((front.requested_w != temp.requested_w) ||
+ (front.requested_h != temp.requested_h)) {
+ // the size changed, we need to ask our client to request a new buffer
+ LOGD_IF(DEBUG_RESIZE,
+ "resize (layer=%p), requested (%dx%d), "
+ "drawing (%d,%d), (%dx%d), (%dx%d)",
+ this,
+ int(temp.requested_w), int(temp.requested_h),
+ int(front.requested_w), int(front.requested_h),
+ int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()),
+ int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight()));
+
+ // we're being resized and there is a freeze display request,
+ // acquire a freeze lock, so that the screen stays put
+ // until we've redrawn at the new size; this is to avoid
+ // glitches upon orientation changes.
+ if (mFlinger->hasFreezeRequest()) {
+ // if the surface is hidden, don't try to acquire the
+ // freeze lock, since hidden surfaces may never redraw
+ if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
+ mFreezeLock = mFlinger->getFreezeLock();
+ }
+ }
+
+ // this will make sure LayerBase::doTransaction doesn't update
+ // the drawing state's size
+ Layer::State& editDraw(mDrawingState);
+ editDraw.requested_w = temp.requested_w;
+ editDraw.requested_h = temp.requested_h;
+
+ // record the new size, form this point on, when the client request a
+ // buffer, it'll get the new size.
+ setDrawingSize(temp.requested_w, temp.requested_h);
+
+ // all buffers need reallocation
+ lcblk->reallocate();
+ }
+
+ if (temp.sequence != front.sequence) {
+ if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) {
+ // this surface is now hidden, so it shouldn't hold a freeze lock
+ // (it may never redraw, which is fine if it is hidden)
+ mFreezeLock.clear();
+ }
+ }
+
+ return LayerBase::doTransaction(flags);
+}
+
+void Layer::setDrawingSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mLock);
+ mWidth = w;
+ mHeight = h;
+}
+
+// ----------------------------------------------------------------------------
+// pageflip handling...
+// ----------------------------------------------------------------------------
+
+void Layer::lockPageFlip(bool& recomputeVisibleRegions)
+{
+ ssize_t buf = lcblk->retireAndLock();
+ if (buf < NO_ERROR) {
+ //LOGW("nothing to retire (%s)", strerror(-buf));
+ // NOTE: here the buffer is locked because we will used
+ // for composition later in the loop
+ return;
+ }
+
+ // ouch, this really should never happen
+ if (uint32_t(buf)>=NUM_BUFFERS) {
+ LOGE("retireAndLock() buffer index (%d) out of range", buf);
+ mPostedDirtyRegion.clear();
+ return;
+ }
+
+ // we retired a buffer, which becomes the new front buffer
+ mFrontBufferIndex = buf;
+
+ // get the dirty region
+ sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
+ if (newFrontBuffer != NULL) {
+ // compute the posted region
+ const Region dirty(lcblk->getDirtyRegion(buf));
+ mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
+
+ // update the layer size and release freeze-lock
+ const Layer::State& front(drawingState());
+ if (newFrontBuffer->getWidth() == front.requested_w &&
+ newFrontBuffer->getHeight() == front.requested_h)
+ {
+ if ((front.w != front.requested_w) ||
+ (front.h != front.requested_h))
+ {
+ // Here we pretend the transaction happened by updating the
+ // current and drawing states. Drawing state is only accessed
+ // in this thread, no need to have it locked
+ Layer::State& editDraw(mDrawingState);
+ editDraw.w = editDraw.requested_w;
+ editDraw.h = editDraw.requested_h;
+
+ // We also need to update the current state so that we don't
+ // end-up doing too much work during the next transaction.
+ // NOTE: We actually don't need hold the transaction lock here
+ // because State::w and State::h are only accessed from
+ // this thread
+ Layer::State& editTemp(currentState());
+ editTemp.w = editDraw.w;
+ editTemp.h = editDraw.h;
+
+ // recompute visible region
+ recomputeVisibleRegions = true;
+ }
+
+ // we now have the correct size, unfreeze the screen
+ mFreezeLock.clear();
+ }
+ } else {
+ // this should not happen unless we ran out of memory while
+ // allocating the buffer. we're hoping that things will get back
+ // to normal the next time the app tries to draw into this buffer.
+ // meanwhile, pretend the screen didn't update.
+ mPostedDirtyRegion.clear();
+ }
+
+ if (lcblk->getQueuedCount()) {
+ // signal an event if we have more buffers waiting
+ mFlinger->signalEvent();
+ }
+
+ if (!mPostedDirtyRegion.isEmpty()) {
+ reloadTexture( mPostedDirtyRegion );
+ }
+}
+
+void Layer::unlockPageFlip(
+ const Transform& planeTransform, Region& outDirtyRegion)
+{
+ Region dirtyRegion(mPostedDirtyRegion);
+ if (!dirtyRegion.isEmpty()) {
+ mPostedDirtyRegion.clear();
+ // The dirty region is given in the layer's coordinate space
+ // transform the dirty region by the surface's transformation
+ // and the global transformation.
+ const Layer::State& s(drawingState());
+ const Transform tr(planeTransform * s.transform);
+ dirtyRegion = tr.transform(dirtyRegion);
+
+ // At this point, the dirty region is in screen space.
+ // Make sure it's constrained by the visible region (which
+ // is in screen space as well).
+ dirtyRegion.andSelf(visibleRegionScreen);
+ outDirtyRegion.orSelf(dirtyRegion);
+ }
+ if (visibleRegionScreen.isEmpty()) {
+ // an invisible layer should not hold a freeze-lock
+ // (because it may never be updated and thereore never release it)
+ mFreezeLock.clear();
+ }
+}
+
+void Layer::finishPageFlip()
+{
+ status_t err = lcblk->unlock( mFrontBufferIndex );
+ LOGE_IF(err!=NO_ERROR,
+ "layer %p, buffer=%d wasn't locked!",
+ this, mFrontBufferIndex);
+}
+
+// ---------------------------------------------------------------------------
+
+Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<Layer>& owner)
+ : Surface(flinger, id, owner->getIdentity(), owner)
+{
+}
+
+Layer::SurfaceLayer::~SurfaceLayer()
+{
+}
+
+sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage)
+{
+ sp<GraphicBuffer> buffer;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ LOGE_IF(uint32_t(index)>=NUM_BUFFERS,
+ "getBuffer() index (%d) out of range", index);
+ if (uint32_t(index) < NUM_BUFFERS) {
+ buffer = owner->requestBuffer(index, usage);
+ }
+ }
+ return buffer;
+}
+
+// ---------------------------------------------------------------------------
+
+
+}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
new file mode 100644
index 0000000..743afb4
--- /dev/null
+++ b/services/surfaceflinger/Layer.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_H
+#define ANDROID_LAYER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+#include <pixelflinger/pixelflinger.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "LayerBase.h"
+#include "Transform.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class Client;
+class FreezeLock;
+
+// ---------------------------------------------------------------------------
+
+const size_t NUM_BUFFERS = 2;
+
+class Layer : public LayerBaseClient
+{
+public:
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i);
+
+ virtual ~Layer();
+
+ status_t setBuffers(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags=0);
+
+ void setDrawingSize(uint32_t w, uint32_t h);
+
+ virtual void onDraw(const Region& clip) const;
+ virtual uint32_t doTransaction(uint32_t transactionFlags);
+ virtual void lockPageFlip(bool& recomputeVisibleRegions);
+ virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+ virtual void finishPageFlip();
+ virtual bool needsBlending() const { return mNeedsBlending; }
+ virtual bool needsDithering() const { return mNeedsDithering; }
+ virtual bool isSecure() const { return mSecure; }
+ virtual sp<Surface> createSurface() const;
+ virtual status_t ditch();
+
+ // only for debugging
+ inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; }
+ // only for debugging
+ inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
+ // only for debugging
+ inline PixelFormat pixelFormat() const { return mFormat; }
+ // only for debugging
+ inline int getFrontBufferIndex() const { return mFrontBufferIndex; }
+
+private:
+ inline sp<GraphicBuffer> getFrontBufferLocked() {
+ return mBuffers[mFrontBufferIndex];
+ }
+
+ void reloadTexture(const Region& dirty);
+
+ uint32_t getEffectiveUsage(uint32_t usage) const;
+
+ sp<GraphicBuffer> requestBuffer(int index, int usage);
+ void destroy();
+
+ class SurfaceLayer : public LayerBaseClient::Surface {
+ public:
+ SurfaceLayer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<Layer>& owner);
+ ~SurfaceLayer();
+ private:
+ virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ sp<Layer> getOwner() const {
+ return static_cast<Layer*>(Surface::getOwner().get());
+ }
+ };
+ friend class SurfaceLayer;
+
+ sp<Surface> mSurface;
+
+ bool mSecure;
+ bool mNoEGLImageForSwBuffers;
+ int32_t mFrontBufferIndex;
+ bool mNeedsBlending;
+ bool mNeedsDithering;
+ Region mPostedDirtyRegion;
+ sp<FreezeLock> mFreezeLock;
+ PixelFormat mFormat;
+
+ // protected by mLock
+ sp<GraphicBuffer> mBuffers[NUM_BUFFERS];
+ Texture mTextures[NUM_BUFFERS];
+ sp<GraphicBuffer> mHybridBuffer;
+ uint32_t mWidth;
+ uint32_t mHeight;
+
+ mutable Mutex mLock;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_H
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
new file mode 100644
index 0000000..a8b735e
--- /dev/null
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <hardware/hardware.h>
+
+#include "clz.h"
+#include "LayerBase.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+const uint32_t LayerBase::typeInfo = 1;
+const char* const LayerBase::typeID = "LayerBase";
+
+const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2;
+const char* const LayerBaseClient::typeID = "LayerBaseClient";
+
+// ---------------------------------------------------------------------------
+
+LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
+ : dpy(display), contentDirty(false),
+ mFlinger(flinger),
+ mTransformed(false),
+ mUseLinearFiltering(false),
+ mOrientation(0),
+ mLeft(0), mTop(0),
+ mTransactionFlags(0),
+ mPremultipliedAlpha(true), mDebug(false),
+ mInvalidate(0)
+{
+ const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
+ mFlags = hw.getFlags();
+}
+
+LayerBase::~LayerBase()
+{
+}
+
+void LayerBase::setName(const String8& name) {
+ mName = name;
+}
+
+String8 LayerBase::getName() const {
+ return mName;
+}
+
+const GraphicPlane& LayerBase::graphicPlane(int dpy) const
+{
+ return mFlinger->graphicPlane(dpy);
+}
+
+GraphicPlane& LayerBase::graphicPlane(int dpy)
+{
+ return mFlinger->graphicPlane(dpy);
+}
+
+void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags)
+{
+ uint32_t layerFlags = 0;
+ if (flags & ISurfaceComposer::eHidden)
+ layerFlags = ISurfaceComposer::eLayerHidden;
+
+ if (flags & ISurfaceComposer::eNonPremultiplied)
+ mPremultipliedAlpha = false;
+
+ mCurrentState.z = 0;
+ mCurrentState.w = w;
+ mCurrentState.h = h;
+ mCurrentState.requested_w = w;
+ mCurrentState.requested_h = h;
+ mCurrentState.alpha = 0xFF;
+ mCurrentState.flags = layerFlags;
+ mCurrentState.sequence = 0;
+ mCurrentState.transform.set(0, 0);
+
+ // drawing state & current state are identical
+ mDrawingState = mCurrentState;
+}
+
+void LayerBase::commitTransaction() {
+ mDrawingState = mCurrentState;
+}
+void LayerBase::forceVisibilityTransaction() {
+ // this can be called without SurfaceFlinger.mStateLock, but if we
+ // can atomically increment the sequence number, it doesn't matter.
+ android_atomic_inc(&mCurrentState.sequence);
+ requestTransaction();
+}
+bool LayerBase::requestTransaction() {
+ int32_t old = setTransactionFlags(eTransactionNeeded);
+ return ((old & eTransactionNeeded) == 0);
+}
+uint32_t LayerBase::getTransactionFlags(uint32_t flags) {
+ return android_atomic_and(~flags, &mTransactionFlags) & flags;
+}
+uint32_t LayerBase::setTransactionFlags(uint32_t flags) {
+ return android_atomic_or(flags, &mTransactionFlags);
+}
+
+bool LayerBase::setPosition(int32_t x, int32_t y) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
+ return false;
+ mCurrentState.sequence++;
+ mCurrentState.transform.set(x, y);
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setLayer(uint32_t z) {
+ if (mCurrentState.z == z)
+ return false;
+ mCurrentState.sequence++;
+ mCurrentState.z = z;
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setSize(uint32_t w, uint32_t h) {
+ if (mCurrentState.requested_w == w && mCurrentState.requested_h == h)
+ return false;
+ mCurrentState.requested_w = w;
+ mCurrentState.requested_h = h;
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setAlpha(uint8_t alpha) {
+ if (mCurrentState.alpha == alpha)
+ return false;
+ mCurrentState.sequence++;
+ mCurrentState.alpha = alpha;
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) {
+ // TODO: check the matrix has changed
+ mCurrentState.sequence++;
+ mCurrentState.transform.set(
+ matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy);
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setTransparentRegionHint(const Region& transparent) {
+ // TODO: check the region has changed
+ mCurrentState.sequence++;
+ mCurrentState.transparentRegion = transparent;
+ requestTransaction();
+ return true;
+}
+bool LayerBase::setFlags(uint8_t flags, uint8_t mask) {
+ const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
+ if (mCurrentState.flags == newFlags)
+ return false;
+ mCurrentState.sequence++;
+ mCurrentState.flags = newFlags;
+ requestTransaction();
+ return true;
+}
+
+Rect LayerBase::visibleBounds() const
+{
+ return mTransformedBounds;
+}
+
+void LayerBase::setVisibleRegion(const Region& visibleRegion) {
+ // always called from main thread
+ visibleRegionScreen = visibleRegion;
+}
+
+void LayerBase::setCoveredRegion(const Region& coveredRegion) {
+ // always called from main thread
+ coveredRegionScreen = coveredRegion;
+}
+
+uint32_t LayerBase::doTransaction(uint32_t flags)
+{
+ const Layer::State& front(drawingState());
+ const Layer::State& temp(currentState());
+
+ if ((front.requested_w != temp.requested_w) ||
+ (front.requested_h != temp.requested_h)) {
+ // resize the layer, set the physical size to the requested size
+ Layer::State& editTemp(currentState());
+ editTemp.w = temp.requested_w;
+ editTemp.h = temp.requested_h;
+ }
+
+ if ((front.w != temp.w) || (front.h != temp.h)) {
+ // invalidate and recompute the visible regions if needed
+ flags |= Layer::eVisibleRegion;
+ }
+
+ if (temp.sequence != front.sequence) {
+ // invalidate and recompute the visible regions if needed
+ flags |= eVisibleRegion;
+ this->contentDirty = true;
+
+ const bool linearFiltering = mUseLinearFiltering;
+ mUseLinearFiltering = false;
+ if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
+ // we may use linear filtering, if the matrix scales us
+ const uint8_t type = temp.transform.getType();
+ if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) {
+ mUseLinearFiltering = true;
+ }
+ }
+ }
+
+ // Commit the transaction
+ commitTransaction();
+ return flags;
+}
+
+void LayerBase::validateVisibility(const Transform& planeTransform)
+{
+ const Layer::State& s(drawingState());
+ const Transform tr(planeTransform * s.transform);
+ const bool transformed = tr.transformed();
+
+ uint32_t w = s.w;
+ uint32_t h = s.h;
+ tr.transform(mVertices[0], 0, 0);
+ tr.transform(mVertices[1], 0, h);
+ tr.transform(mVertices[2], w, h);
+ tr.transform(mVertices[3], w, 0);
+ if (UNLIKELY(transformed)) {
+ // NOTE: here we could also punt if we have too many rectangles
+ // in the transparent region
+ if (tr.preserveRects()) {
+ // transform the transparent region
+ transparentRegionScreen = tr.transform(s.transparentRegion);
+ } else {
+ // transformation too complex, can't do the transparent region
+ // optimization.
+ transparentRegionScreen.clear();
+ }
+ } else {
+ transparentRegionScreen = s.transparentRegion;
+ }
+
+ // cache a few things...
+ mOrientation = tr.getOrientation();
+ mTransformedBounds = tr.makeBounds(w, h);
+ mTransformed = transformed;
+ mLeft = tr.tx();
+ mTop = tr.ty();
+}
+
+void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
+{
+}
+
+void LayerBase::unlockPageFlip(
+ const Transform& planeTransform, Region& outDirtyRegion)
+{
+ if ((android_atomic_and(~1, &mInvalidate)&1) == 1) {
+ outDirtyRegion.orSelf(visibleRegionScreen);
+ }
+}
+
+void LayerBase::finishPageFlip()
+{
+}
+
+void LayerBase::invalidate()
+{
+ if ((android_atomic_or(1, &mInvalidate)&1) == 0) {
+ mFlinger->signalEvent();
+ }
+}
+
+void LayerBase::drawRegion(const Region& reg) const
+{
+ Region::const_iterator it = reg.begin();
+ Region::const_iterator const end = reg.end();
+ if (it != end) {
+ Rect r;
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const int32_t fbWidth = hw.getWidth();
+ const int32_t fbHeight = hw.getHeight();
+ const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 },
+ { fbWidth, fbHeight }, { 0, fbHeight } };
+ glVertexPointer(2, GL_SHORT, 0, vertices);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ }
+}
+
+void LayerBase::draw(const Region& inClip) const
+{
+ // invalidate the region we'll update
+ Region clip(inClip); // copy-on-write, so no-op most of the time
+
+ // Remove the transparent area from the clipping region
+ const State& s = drawingState();
+ if (LIKELY(!s.transparentRegion.isEmpty())) {
+ clip.subtract(transparentRegionScreen);
+ if (clip.isEmpty()) {
+ // usually this won't happen because this should be taken care of
+ // by SurfaceFlinger::computeVisibleRegions()
+ return;
+ }
+ }
+
+ // reset GL state
+ glEnable(GL_SCISSOR_TEST);
+
+ onDraw(clip);
+
+ /*
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4x(0, 0x8000, 0, 0x10000);
+ drawRegion(transparentRegionScreen);
+ glDisable(GL_BLEND);
+ */
+}
+
+GLuint LayerBase::createTexture() const
+{
+ GLuint textureName = -1;
+ glGenTextures(1, &textureName);
+ glBindTexture(GL_TEXTURE_2D, textureName);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ return textureName;
+}
+
+void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red,
+ GLclampx green, GLclampx blue,
+ GLclampx alpha) const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t fbHeight = hw.getHeight();
+ glColor4x(red,green,blue,alpha);
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glDisable(GL_DITHER);
+
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ glEnable(GL_SCISSOR_TEST);
+ glVertexPointer(2, GL_FIXED, 0, mVertices);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+}
+
+void LayerBase::clearWithOpenGL(const Region& clip) const
+{
+ clearWithOpenGL(clip,0,0,0,0);
+}
+
+void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t fbHeight = hw.getHeight();
+ const State& s(drawingState());
+
+ // bind our texture
+ validateTexture(texture.name);
+ uint32_t width = texture.width;
+ uint32_t height = texture.height;
+
+ glEnable(GL_TEXTURE_2D);
+
+ if (UNLIKELY(s.alpha < 0xFF)) {
+ // We have an alpha-modulation. We need to modulate all
+ // texture components by alpha because we're always using
+ // premultiplied alpha.
+
+ // If the texture doesn't have an alpha channel we can
+ // use REPLACE and switch to non premultiplied alpha
+ // blending (SRCA/ONE_MINUS_SRCA).
+
+ GLenum env, src;
+ if (needsBlending()) {
+ env = GL_MODULATE;
+ src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
+ } else {
+ env = GL_REPLACE;
+ src = GL_SRC_ALPHA;
+ }
+ const GGLfixed alpha = (s.alpha << 16)/255;
+ glColor4x(alpha, alpha, alpha, alpha);
+ glEnable(GL_BLEND);
+ glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env);
+ } else {
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
+ if (needsBlending()) {
+ GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA;
+ glEnable(GL_BLEND);
+ glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glDisable(GL_BLEND);
+ }
+ }
+
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+
+ //StopWatch watch("GL transformed");
+ const GLfixed texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 0x10000 },
+ { 0x10000, 0x10000 },
+ { 0x10000, 0 }
+ };
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+
+ // the texture's source is rotated
+ switch (texture.transform) {
+ case HAL_TRANSFORM_ROT_90:
+ glTranslatef(0, 1, 0);
+ glRotatef(-90, 0, 0, 1);
+ break;
+ case HAL_TRANSFORM_ROT_180:
+ glTranslatef(1, 1, 0);
+ glRotatef(-180, 0, 0, 1);
+ break;
+ case HAL_TRANSFORM_ROT_270:
+ glTranslatef(1, 0, 0);
+ glRotatef(-270, 0, 0, 1);
+ break;
+ }
+
+ if (texture.NPOTAdjust) {
+ glScalef(texture.wScale, texture.hScale, 1.0f);
+ }
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FIXED, 0, mVertices);
+ glTexCoordPointer(2, GL_FIXED, 0, texCoords);
+
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LayerBase::validateTexture(GLint textureName) const
+{
+ glBindTexture(GL_TEXTURE_2D, textureName);
+ // TODO: reload the texture if needed
+ // this is currently done in loadTexture() below
+ if (mUseLinearFiltering) {
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ } else {
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ if (needsDithering()) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+}
+
+bool LayerBase::isSupportedYuvFormat(int format) const
+{
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_P:
+ case HAL_PIXEL_FORMAT_YCbCr_420_P:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case HAL_PIXEL_FORMAT_YCbCr_420_I:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return true;
+ }
+ return false;
+}
+
+void LayerBase::loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t) const
+{
+ if (texture->name == -1U) {
+ // uh?
+ return;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, texture->name);
+
+ /*
+ * In OpenGL ES we can't specify a stride with glTexImage2D (however,
+ * GL_UNPACK_ALIGNMENT is a limited form of stride).
+ * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
+ * need to do something reasonable (here creating a bigger texture).
+ *
+ * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
+ *
+ * This situation doesn't happen often, but some h/w have a limitation
+ * for their framebuffer (eg: must be multiple of 8 pixels), and
+ * we need to take that into account when using these buffers as
+ * textures.
+ *
+ * This should never be a problem with POT textures
+ */
+
+ int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
+ unpack = 1 << ((unpack > 3) ? 3 : unpack);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
+
+ /*
+ * round to POT if needed
+ */
+ if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
+ texture->NPOTAdjust = true;
+ }
+
+ if (texture->NPOTAdjust) {
+ // find the smallest power-of-two that will accommodate our surface
+ texture->potWidth = 1 << (31 - clz(t.width));
+ texture->potHeight = 1 << (31 - clz(t.height));
+ if (texture->potWidth < t.width) texture->potWidth <<= 1;
+ if (texture->potHeight < t.height) texture->potHeight <<= 1;
+ texture->wScale = float(t.width) / texture->potWidth;
+ texture->hScale = float(t.height) / texture->potHeight;
+ } else {
+ texture->potWidth = t.width;
+ texture->potHeight = t.height;
+ }
+
+ Rect bounds(dirty.bounds());
+ GLvoid* data = 0;
+ if (texture->width != t.width || texture->height != t.height) {
+ texture->width = t.width;
+ texture->height = t.height;
+
+ // texture size changed, we need to create a new one
+ bounds.set(Rect(t.width, t.height));
+ if (t.width == texture->potWidth &&
+ t.height == texture->potHeight) {
+ // we can do it one pass
+ data = t.data;
+ }
+
+ if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGB, texture->potWidth, texture->potHeight, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture->potWidth, texture->potHeight, 0,
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
+ t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture->potWidth, texture->potHeight, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+ } else if (isSupportedYuvFormat(t.format)) {
+ // just show the Y plane of YUV buffers
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
+ } else {
+ // oops, we don't handle this format!
+ LOGE("layer %p, texture=%d, using format %d, which is not "
+ "supported by the GL", this, texture->name, t.format);
+ }
+ }
+ if (!data) {
+ if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
+ t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride*4);
+ } else if (isSupportedYuvFormat(t.format)) {
+ // just show the Y plane of YUV buffers
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride);
+ }
+ }
+}
+
+status_t LayerBase::initializeEglImage(
+ const sp<GraphicBuffer>& buffer, Texture* texture)
+{
+ status_t err = NO_ERROR;
+
+ // we need to recreate the texture
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+
+ // free the previous image
+ if (texture->image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, texture->image);
+ texture->image = EGL_NO_IMAGE_KHR;
+ }
+
+ // construct an EGL_NATIVE_BUFFER_ANDROID
+ android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+
+ // create the new EGLImageKHR
+ const EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE, EGL_NONE
+ };
+ texture->image = eglCreateImageKHR(
+ dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)clientBuf, attrs);
+
+ if (texture->image != EGL_NO_IMAGE_KHR) {
+ glBindTexture(GL_TEXTURE_2D, texture->name);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
+ (GLeglImageOES)texture->image);
+ GLint error = glGetError();
+ if (UNLIKELY(error != GL_NO_ERROR)) {
+ LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) "
+ "failed err=0x%04x",
+ this, texture->image, error);
+ err = INVALID_OPERATION;
+ } else {
+ // Everything went okay!
+ texture->NPOTAdjust = false;
+ texture->dirty = false;
+ texture->width = clientBuf->width;
+ texture->height = clientBuf->height;
+ }
+ } else {
+ LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x",
+ this, eglGetError());
+ err = INVALID_OPERATION;
+ }
+ return err;
+}
+
+
+// ---------------------------------------------------------------------------
+
+int32_t LayerBaseClient::sIdentity = 0;
+
+LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i)
+ : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),
+ mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
+{
+ lcblk = new SharedBufferServer(
+ client->ctrlblk, i, NUM_BUFFERS,
+ mIdentity);
+}
+
+void LayerBaseClient::onFirstRef()
+{
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
+ client->bindLayer(this, mIndex);
+ }
+}
+
+LayerBaseClient::~LayerBaseClient()
+{
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
+ client->free(mIndex);
+ }
+ delete lcblk;
+}
+
+int32_t LayerBaseClient::serverIndex() const
+{
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
+ return (client->cid<<16)|mIndex;
+ }
+ return 0xFFFF0000 | mIndex;
+}
+
+sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
+{
+ sp<Surface> s;
+ Mutex::Autolock _l(mLock);
+ s = mClientSurface.promote();
+ if (s == 0) {
+ s = createSurface();
+ mClientSurface = s;
+ }
+ return s;
+}
+
+sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
+{
+ return new Surface(mFlinger, clientIndex(), mIdentity,
+ const_cast<LayerBaseClient *>(this));
+}
+
+// called with SurfaceFlinger::mStateLock as soon as the layer is entered
+// in the purgatory list
+void LayerBaseClient::onRemoved()
+{
+ // wake up the condition
+ lcblk->setStatus(NO_INIT);
+}
+
+// ---------------------------------------------------------------------------
+
+LayerBaseClient::Surface::Surface(
+ const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, int identity,
+ const sp<LayerBaseClient>& owner)
+ : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
+{
+}
+
+LayerBaseClient::Surface::~Surface()
+{
+ /*
+ * This is a good place to clean-up all client resources
+ */
+
+ // destroy client resources
+ sp<LayerBaseClient> layer = getOwner();
+ if (layer != 0) {
+ mFlinger->destroySurface(layer);
+ }
+}
+
+sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const {
+ sp<LayerBaseClient> owner(mOwner.promote());
+ return owner;
+}
+
+status_t LayerBaseClient::Surface::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case REGISTER_BUFFERS:
+ case UNREGISTER_BUFFERS:
+ case CREATE_OVERLAY:
+ {
+ if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ }
+ }
+ return BnSurface::onTransact(code, data, reply, flags);
+}
+
+sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage)
+{
+ return NULL;
+}
+
+status_t LayerBaseClient::Surface::registerBuffers(
+ const ISurface::BufferHeap& buffers)
+{
+ return INVALID_OPERATION;
+}
+
+void LayerBaseClient::Surface::postBuffer(ssize_t offset)
+{
+}
+
+void LayerBaseClient::Surface::unregisterBuffers()
+{
+}
+
+sp<OverlayRef> LayerBaseClient::Surface::createOverlay(
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation)
+{
+ return NULL;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
new file mode 100644
index 0000000..62ec839
--- /dev/null
+++ b/services/surfaceflinger/LayerBase.h
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_BASE_H
+#define ANDROID_LAYER_BASE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+
+#include <utils/RefBase.h>
+
+#include <ui/Region.h>
+#include <ui/Overlay.h>
+
+#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <private/surfaceflinger/SharedBufferStack.h>
+#include <private/surfaceflinger/LayerState.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include "Transform.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class DisplayHardware;
+class Client;
+class GraphicBuffer;
+class GraphicPlane;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+class LayerBase : public RefBase
+{
+ // poor man's dynamic_cast below
+ template<typename T>
+ struct getTypeInfoOfAnyType {
+ static uint32_t get() { return T::typeInfo; }
+ };
+
+ template<typename T>
+ struct getTypeInfoOfAnyType<T*> {
+ static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); }
+ };
+
+public:
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ template<typename T>
+ static T dynamicCast(LayerBase* base) {
+ uint32_t mostDerivedInfo = base->getTypeInfo();
+ uint32_t castToInfo = getTypeInfoOfAnyType<T>::get();
+ if ((mostDerivedInfo & castToInfo) == castToInfo)
+ return static_cast<T>(base);
+ return 0;
+ }
+
+
+ LayerBase(SurfaceFlinger* flinger, DisplayID display);
+
+ DisplayID dpy;
+ mutable bool contentDirty;
+ Region visibleRegionScreen;
+ Region transparentRegionScreen;
+ Region coveredRegionScreen;
+
+ struct State {
+ uint32_t w;
+ uint32_t h;
+ uint32_t requested_w;
+ uint32_t requested_h;
+ uint32_t z;
+ uint8_t alpha;
+ uint8_t flags;
+ uint8_t reserved[2];
+ int32_t sequence; // changes when visible regions can change
+ uint32_t tint;
+ Transform transform;
+ Region transparentRegion;
+ };
+
+ void setName(const String8& name);
+ String8 getName() const;
+
+ // modify current state
+ bool setPosition(int32_t x, int32_t y);
+ bool setLayer(uint32_t z);
+ bool setSize(uint32_t w, uint32_t h);
+ bool setAlpha(uint8_t alpha);
+ bool setMatrix(const layer_state_t::matrix22_t& matrix);
+ bool setTransparentRegionHint(const Region& opaque);
+ bool setFlags(uint8_t flags, uint8_t mask);
+
+ void commitTransaction();
+ bool requestTransaction();
+ void forceVisibilityTransaction();
+
+ uint32_t getTransactionFlags(uint32_t flags);
+ uint32_t setTransactionFlags(uint32_t flags);
+
+ Rect visibleBounds() const;
+ void drawRegion(const Region& reg) const;
+
+ void invalidate();
+
+ /**
+ * draw - performs some global clipping optimizations
+ * and calls onDraw().
+ * Typically this method is not overridden, instead implement onDraw()
+ * to perform the actual drawing.
+ */
+ virtual void draw(const Region& clip) const;
+
+ /**
+ * onDraw - draws the surface.
+ */
+ virtual void onDraw(const Region& clip) const = 0;
+
+ /**
+ * initStates - called just after construction
+ */
+ virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
+
+ /**
+ * doTransaction - process the transaction. This is a good place to figure
+ * out which attributes of the surface have changed.
+ */
+ virtual uint32_t doTransaction(uint32_t transactionFlags);
+
+ /**
+ * setVisibleRegion - called to set the new visible region. This gives
+ * a chance to update the new visible region or record the fact it changed.
+ */
+ virtual void setVisibleRegion(const Region& visibleRegion);
+
+ /**
+ * setCoveredRegion - called when the covered region changes. The covered
+ * region corresponds to any area of the surface that is covered
+ * (transparently or not) by another surface.
+ */
+ virtual void setCoveredRegion(const Region& coveredRegion);
+
+ /**
+ * validateVisibility - cache a bunch of things
+ */
+ virtual void validateVisibility(const Transform& globalTransform);
+
+ /**
+ * lockPageFlip - called each time the screen is redrawn and returns whether
+ * the visible regions need to be recomputed (this is a fairly heavy
+ * operation, so this should be set only if needed). Typically this is used
+ * to figure out if the content or size of a surface has changed.
+ */
+ virtual void lockPageFlip(bool& recomputeVisibleRegions);
+
+ /**
+ * unlockPageFlip - called each time the screen is redrawn. updates the
+ * final dirty region wrt the planeTransform.
+ * At this point, all visible regions, surface position and size, etc... are
+ * correct.
+ */
+ virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+
+ /**
+ * finishPageFlip - called after all surfaces have drawn.
+ */
+ virtual void finishPageFlip();
+
+ /**
+ * needsBlending - true if this surface needs blending
+ */
+ virtual bool needsBlending() const { return false; }
+
+ /**
+ * needsDithering - true if this surface needs dithering
+ */
+ virtual bool needsDithering() const { return false; }
+
+ /**
+ * transformed -- true is this surface needs a to be transformed
+ */
+ virtual bool transformed() const { return mTransformed; }
+
+ /**
+ * isSecure - true if this surface is secure, that is if it prevents
+ * screenshots or VNC servers.
+ */
+ virtual bool isSecure() const { return false; }
+
+ /** Called from the main thread, when the surface is removed from the
+ * draw list */
+ virtual status_t ditch() { return NO_ERROR; }
+
+ /** called with the state lock when the surface is removed from the
+ * current list */
+ virtual void onRemoved() { };
+
+
+ enum { // flags for doTransaction()
+ eVisibleRegion = 0x00000002,
+ };
+
+
+ inline const State& drawingState() const { return mDrawingState; }
+ inline const State& currentState() const { return mCurrentState; }
+ inline State& currentState() { return mCurrentState; }
+
+ static int compareCurrentStateZ(
+ sp<LayerBase> const * layerA,
+ sp<LayerBase> const * layerB) {
+ return layerA[0]->currentState().z - layerB[0]->currentState().z;
+ }
+
+ int32_t getOrientation() const { return mOrientation; }
+ int tx() const { return mLeft; }
+ int ty() const { return mTop; }
+
+protected:
+ const GraphicPlane& graphicPlane(int dpy) const;
+ GraphicPlane& graphicPlane(int dpy);
+
+ GLuint createTexture() const;
+
+ struct Texture {
+ Texture() : name(-1U), width(0), height(0),
+ image(EGL_NO_IMAGE_KHR), transform(0),
+ NPOTAdjust(false), dirty(true) { }
+ GLuint name;
+ GLuint width;
+ GLuint height;
+ GLuint potWidth;
+ GLuint potHeight;
+ GLfloat wScale;
+ GLfloat hScale;
+ EGLImageKHR image;
+ uint32_t transform;
+ bool NPOTAdjust;
+ bool dirty;
+ };
+
+ void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g,
+ GLclampx b, GLclampx alpha) const;
+ void clearWithOpenGL(const Region& clip) const;
+ void drawWithOpenGL(const Region& clip, const Texture& texture) const;
+ void loadTexture(Texture* texture,
+ const Region& dirty, const GGLSurface& t) const;
+ status_t initializeEglImage(
+ const sp<GraphicBuffer>& buffer, Texture* texture);
+
+ bool isSupportedYuvFormat(int format) const;
+
+ sp<SurfaceFlinger> mFlinger;
+ uint32_t mFlags;
+
+ // cached during validateVisibility()
+ bool mTransformed;
+ bool mUseLinearFiltering;
+ int32_t mOrientation;
+ GLfixed mVertices[4][2];
+ Rect mTransformedBounds;
+ int mLeft;
+ int mTop;
+
+ // these are protected by an external lock
+ State mCurrentState;
+ State mDrawingState;
+ volatile int32_t mTransactionFlags;
+
+ // don't change, don't need a lock
+ bool mPremultipliedAlpha;
+ String8 mName;
+ mutable bool mDebug;
+
+
+ // atomic
+ volatile int32_t mInvalidate;
+
+
+protected:
+ virtual ~LayerBase();
+
+private:
+ LayerBase(const LayerBase& rhs);
+ void validateTexture(GLint textureName) const;
+};
+
+
+// ---------------------------------------------------------------------------
+
+class LayerBaseClient : public LayerBase
+{
+public:
+ class Surface;
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ // lcblk is (almost) only accessed from the main SF thread, in the places
+ // where it's not, a reference to Client must be held
+ SharedBufferServer* lcblk;
+
+ LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i);
+ virtual ~LayerBaseClient();
+ virtual void onFirstRef();
+
+ const wp<Client> client;
+
+ inline uint32_t getIdentity() const { return mIdentity; }
+ inline int32_t clientIndex() const { return mIndex; }
+ int32_t serverIndex() const;
+
+
+ sp<Surface> getSurface();
+ virtual sp<Surface> createSurface() const;
+
+ virtual void onRemoved();
+
+
+ class Surface : public BnSurface
+ {
+ public:
+ int32_t getToken() const { return mToken; }
+ int32_t getIdentity() const { return mIdentity; }
+
+ protected:
+ Surface(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, int identity,
+ const sp<LayerBaseClient>& owner);
+ virtual ~Surface();
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+ sp<LayerBaseClient> getOwner() const;
+
+ private:
+ virtual sp<GraphicBuffer> requestBuffer(int index, int usage);
+ virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+ virtual sp<OverlayRef> createOverlay(uint32_t w, uint32_t h,
+ int32_t format, int32_t orientation);
+
+ protected:
+ friend class LayerBaseClient;
+ sp<SurfaceFlinger> mFlinger;
+ int32_t mToken;
+ int32_t mIdentity;
+ wp<LayerBaseClient> mOwner;
+ };
+
+ friend class Surface;
+
+private:
+ int32_t mIndex;
+ mutable Mutex mLock;
+ mutable wp<Surface> mClientSurface;
+ // only read
+ const uint32_t mIdentity;
+ static int32_t sIdentity;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_BASE_H
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
new file mode 100644
index 0000000..5fd7904
--- /dev/null
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "clz.h"
+#include "BlurFilter.h"
+#include "LayerBlur.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8;
+const char* const LayerBlur::typeID = "LayerBlur";
+
+// ---------------------------------------------------------------------------
+
+LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i)
+ : LayerBaseClient(flinger, display, client, i), mCacheDirty(true),
+ mRefreshCache(true), mCacheAge(0), mTextureName(-1U),
+ mWidthScale(1.0f), mHeightScale(1.0f),
+ mBlurFormat(GGL_PIXEL_FORMAT_RGB_565)
+{
+}
+
+LayerBlur::~LayerBlur()
+{
+ if (mTextureName != -1U) {
+ glDeleteTextures(1, &mTextureName);
+ }
+}
+
+void LayerBlur::setVisibleRegion(const Region& visibleRegion)
+{
+ LayerBaseClient::setVisibleRegion(visibleRegion);
+ if (visibleRegionScreen.isEmpty()) {
+ if (mTextureName != -1U) {
+ // We're not visible, free the texture up.
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &mTextureName);
+ mTextureName = -1U;
+ }
+ }
+}
+
+uint32_t LayerBlur::doTransaction(uint32_t flags)
+{
+ // we're doing a transaction, refresh the cache!
+ if (!mFlinger->isFrozen()) {
+ mRefreshCache = true;
+ mCacheDirty = true;
+ flags |= eVisibleRegion;
+ this->contentDirty = true;
+ }
+ return LayerBase::doTransaction(flags);
+}
+
+void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion)
+{
+ // this code-path must be as tight as possible, it's called each time
+ // the screen is composited.
+ if (UNLIKELY(!visibleRegionScreen.isEmpty())) {
+ // if anything visible below us is invalidated, the cache becomes dirty
+ if (!mCacheDirty &&
+ !visibleRegionScreen.intersect(outDirtyRegion).isEmpty()) {
+ mCacheDirty = true;
+ }
+ if (mCacheDirty) {
+ if (!mFlinger->isFrozen()) {
+ // update everything below us that is visible
+ outDirtyRegion.orSelf(visibleRegionScreen);
+ nsecs_t now = systemTime();
+ if ((now - mCacheAge) >= ms2ns(500)) {
+ mCacheAge = now;
+ mRefreshCache = true;
+ mCacheDirty = false;
+ } else {
+ if (!mAutoRefreshPending) {
+ mFlinger->signalDelayedEvent(ms2ns(500));
+ mAutoRefreshPending = true;
+ }
+ }
+ }
+ }
+ }
+ LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);
+}
+
+void LayerBlur::onDraw(const Region& clip) const
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t fbHeight = hw.getHeight();
+ int x = mTransformedBounds.left;
+ int y = mTransformedBounds.top;
+ int w = mTransformedBounds.width();
+ int h = mTransformedBounds.height();
+ GLint X = x;
+ GLint Y = fbHeight - (y + h);
+ if (X < 0) {
+ w += X;
+ X = 0;
+ }
+ if (Y < 0) {
+ h += Y;
+ Y = 0;
+ }
+ if (w<0 || h<0) {
+ // we're outside of the framebuffer
+ return;
+ }
+
+ if (mTextureName == -1U) {
+ // create the texture name the first time
+ // can't do that in the ctor, because it runs in another thread.
+ glGenTextures(1, &mTextureName);
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &mReadFormat);
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &mReadType);
+ if (mReadFormat != GL_RGB || mReadType != GL_UNSIGNED_SHORT_5_6_5) {
+ mReadFormat = GL_RGBA;
+ mReadType = GL_UNSIGNED_BYTE;
+ mBlurFormat = GGL_PIXEL_FORMAT_RGBX_8888;
+ }
+ }
+
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (it != end) {
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, mTextureName);
+
+ if (mRefreshCache) {
+ mRefreshCache = false;
+ mAutoRefreshPending = false;
+
+ int32_t pixelSize = 4;
+ int32_t s = w;
+ if (mReadType == GL_UNSIGNED_SHORT_5_6_5) {
+ // allocate enough memory for 4-bytes (2 pixels) aligned data
+ s = (w + 1) & ~1;
+ pixelSize = 2;
+ }
+
+ uint16_t* const pixels = (uint16_t*)malloc(s*h*pixelSize);
+
+ // This reads the frame-buffer, so a h/w GL would have to
+ // finish() its rendering first. we don't want to do that
+ // too often. Read data is 4-bytes aligned.
+ glReadPixels(X, Y, w, h, mReadFormat, mReadType, pixels);
+
+ // blur that texture.
+ GGLSurface bl;
+ bl.version = sizeof(GGLSurface);
+ bl.width = w;
+ bl.height = h;
+ bl.stride = s;
+ bl.format = mBlurFormat;
+ bl.data = (GGLubyte*)pixels;
+ blurFilter(&bl, 8, 2);
+
+ if (mFlags & (DisplayHardware::NPOT_EXTENSION)) {
+ glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0,
+ mReadFormat, mReadType, pixels);
+ mWidthScale = 1.0f / w;
+ mHeightScale =-1.0f / h;
+ mYOffset = 0;
+ } else {
+ GLuint tw = 1 << (31 - clz(w));
+ GLuint th = 1 << (31 - clz(h));
+ if (tw < GLuint(w)) tw <<= 1;
+ if (th < GLuint(h)) th <<= 1;
+ glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, tw, th, 0,
+ mReadFormat, mReadType, NULL);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
+ mReadFormat, mReadType, pixels);
+ mWidthScale = 1.0f / tw;
+ mHeightScale =-1.0f / th;
+ mYOffset = th-h;
+ }
+
+ free((void*)pixels);
+ }
+
+ const State& s = drawingState();
+ if (UNLIKELY(s.alpha < 0xFF)) {
+ const GGLfixed alpha = (s.alpha << 16)/255;
+ glColor4x(0, 0, 0, alpha);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ } else {
+ glDisable(GL_BLEND);
+ }
+
+ if (mFlags & DisplayHardware::SLOW_CONFIG) {
+ glDisable(GL_DITHER);
+ } else {
+ glEnable(GL_DITHER);
+ }
+
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ if (UNLIKELY(transformed()
+ || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) {
+ // This is a very rare scenario.
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glScalef(mWidthScale, mHeightScale, 1);
+ glTranslatef(-x, mYOffset - y, 0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FIXED, 0, mVertices);
+ glTexCoordPointer(2, GL_FIXED, 0, mVertices);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ } else {
+ // NOTE: this is marginally faster with the software gl, because
+ // glReadPixels() reads the fb bottom-to-top, however we'll
+ // skip all the jaccobian computations.
+ Rect r;
+ GLint crop[4] = { 0, 0, w, h };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ y = fbHeight - (y + h);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawTexiOES(x, y, 0, w, h);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h
new file mode 100644
index 0000000..5b63dec
--- /dev/null
+++ b/services/surfaceflinger/LayerBlur.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_BLUR_H
+#define ANDROID_LAYER_BLUR_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <ui/Region.h>
+
+#include "LayerBase.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class LayerBlur : public LayerBaseClient
+{
+public:
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ LayerBlur(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i);
+ virtual ~LayerBlur();
+
+ virtual void onDraw(const Region& clip) const;
+ virtual bool needsBlending() const { return true; }
+ virtual bool isSecure() const { return false; }
+
+ virtual uint32_t doTransaction(uint32_t flags);
+ virtual void setVisibleRegion(const Region& visibleRegion);
+ virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+
+private:
+ bool mCacheDirty;
+ mutable bool mRefreshCache;
+ mutable bool mAutoRefreshPending;
+ nsecs_t mCacheAge;
+ mutable GLuint mTextureName;
+ mutable GLfloat mWidthScale;
+ mutable GLfloat mHeightScale;
+ mutable GLfloat mYOffset;
+ mutable GLint mReadFormat;
+ mutable GLint mReadType;
+ mutable uint32_t mBlurFormat;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_BLUR_H
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
new file mode 100644
index 0000000..5c21593
--- /dev/null
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/StopWatch.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include <hardware/copybit.h>
+
+#include "LayerBuffer.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20;
+const char* const LayerBuffer::typeID = "LayerBuffer";
+gralloc_module_t const* LayerBuffer::sGrallocModule = 0;
+
+// ---------------------------------------------------------------------------
+
+LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i)
+ : LayerBaseClient(flinger, display, client, i),
+ mNeedsBlending(false), mBlitEngine(0)
+{
+}
+
+LayerBuffer::~LayerBuffer()
+{
+ if (mBlitEngine) {
+ copybit_close(mBlitEngine);
+ }
+}
+
+void LayerBuffer::onFirstRef()
+{
+ LayerBaseClient::onFirstRef();
+ mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(),
+ const_cast<LayerBuffer *>(this));
+
+ hw_module_t const* module = (hw_module_t const*)sGrallocModule;
+ if (!module) {
+ // NOTE: technically there is a race here, but it shouldn't
+ // cause any problem since hw_get_module() always returns
+ // the same value.
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
+ sGrallocModule = (gralloc_module_t const *)module;
+ }
+ }
+
+ if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
+ copybit_open(module, &mBlitEngine);
+ }
+}
+
+sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const
+{
+ return mSurface;
+}
+
+status_t LayerBuffer::ditch()
+{
+ mSurface.clear();
+ return NO_ERROR;
+}
+
+bool LayerBuffer::needsBlending() const {
+ return mNeedsBlending;
+}
+
+void LayerBuffer::setNeedsBlending(bool blending) {
+ mNeedsBlending = blending;
+}
+
+void LayerBuffer::postBuffer(ssize_t offset)
+{
+ sp<Source> source(getSource());
+ if (source != 0)
+ source->postBuffer(offset);
+}
+
+void LayerBuffer::unregisterBuffers()
+{
+ sp<Source> source(clearSource());
+ if (source != 0)
+ source->unregisterBuffers();
+}
+
+uint32_t LayerBuffer::doTransaction(uint32_t flags)
+{
+ sp<Source> source(getSource());
+ if (source != 0)
+ source->onTransaction(flags);
+ uint32_t res = LayerBase::doTransaction(flags);
+ // we always want filtering for these surfaces
+ mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
+ return res;
+}
+
+void LayerBuffer::unlockPageFlip(const Transform& planeTransform,
+ Region& outDirtyRegion)
+{
+ // this code-path must be as tight as possible, it's called each time
+ // the screen is composited.
+ sp<Source> source(getSource());
+ if (source != 0)
+ source->onVisibilityResolved(planeTransform);
+ LayerBase::unlockPageFlip(planeTransform, outDirtyRegion);
+}
+
+void LayerBuffer::onDraw(const Region& clip) const
+{
+ sp<Source> source(getSource());
+ if (LIKELY(source != 0)) {
+ source->onDraw(clip);
+ } else {
+ clearWithOpenGL(clip);
+ }
+}
+
+bool LayerBuffer::transformed() const
+{
+ sp<Source> source(getSource());
+ if (LIKELY(source != 0))
+ return source->transformed();
+ return false;
+}
+
+void LayerBuffer::serverDestroy()
+{
+ sp<Source> source(clearSource());
+ if (source != 0) {
+ source->destroy();
+ }
+}
+
+/**
+ * This creates a "buffer" source for this surface
+ */
+status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers)
+{
+ Mutex::Autolock _l(mLock);
+ if (mSource != 0)
+ return INVALID_OPERATION;
+
+ sp<BufferSource> source = new BufferSource(*this, buffers);
+
+ status_t result = source->getStatus();
+ if (result == NO_ERROR) {
+ mSource = source;
+ }
+ return result;
+}
+
+/**
+ * This creates an "overlay" source for this surface
+ */
+sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f,
+ int32_t orientation)
+{
+ sp<OverlayRef> result;
+ Mutex::Autolock _l(mLock);
+ if (mSource != 0)
+ return result;
+
+ sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f, orientation);
+ if (result != 0) {
+ mSource = source;
+ }
+ return result;
+}
+
+sp<LayerBuffer::Source> LayerBuffer::getSource() const {
+ Mutex::Autolock _l(mLock);
+ return mSource;
+}
+
+sp<LayerBuffer::Source> LayerBuffer::clearSource() {
+ sp<Source> source;
+ Mutex::Autolock _l(mLock);
+ source = mSource;
+ mSource.clear();
+ return source;
+}
+
+// ============================================================================
+// LayerBuffer::SurfaceLayerBuffer
+// ============================================================================
+
+LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<LayerBuffer>& owner)
+ : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner)
+{
+}
+
+LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer()
+{
+ unregisterBuffers();
+}
+
+status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers(
+ const ISurface::BufferHeap& buffers)
+{
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
+ return owner->registerBuffers(buffers);
+ return NO_INIT;
+}
+
+void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset)
+{
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
+ owner->postBuffer(offset);
+}
+
+void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers()
+{
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
+ owner->unregisterBuffers();
+}
+
+sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay(
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation) {
+ sp<OverlayRef> result;
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
+ result = owner->createOverlay(w, h, format, orientation);
+ return result;
+}
+
+// ============================================================================
+// LayerBuffer::Buffer
+// ============================================================================
+
+LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers,
+ ssize_t offset, size_t bufferSize)
+ : mBufferHeap(buffers), mSupportsCopybit(false)
+{
+ NativeBuffer& src(mNativeBuffer);
+ src.crop.l = 0;
+ src.crop.t = 0;
+ src.crop.r = buffers.w;
+ src.crop.b = buffers.h;
+
+ src.img.w = buffers.hor_stride ?: buffers.w;
+ src.img.h = buffers.ver_stride ?: buffers.h;
+ src.img.format = buffers.format;
+ src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset);
+ src.img.handle = 0;
+
+ gralloc_module_t const * module = LayerBuffer::getGrallocModule();
+ if (module && module->perform) {
+ int err = module->perform(module,
+ GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,
+ buffers.heap->heapID(), bufferSize,
+ offset, buffers.heap->base(),
+ &src.img.handle);
+
+ // we can fail here is the passed buffer is purely software
+ mSupportsCopybit = (err == NO_ERROR);
+ }
+ }
+
+LayerBuffer::Buffer::~Buffer()
+{
+ NativeBuffer& src(mNativeBuffer);
+ if (src.img.handle) {
+ native_handle_delete(src.img.handle);
+ }
+}
+
+// ============================================================================
+// LayerBuffer::Source
+// LayerBuffer::BufferSource
+// LayerBuffer::OverlaySource
+// ============================================================================
+
+LayerBuffer::Source::Source(LayerBuffer& layer)
+ : mLayer(layer)
+{
+}
+LayerBuffer::Source::~Source() {
+}
+void LayerBuffer::Source::onDraw(const Region& clip) const {
+}
+void LayerBuffer::Source::onTransaction(uint32_t flags) {
+}
+void LayerBuffer::Source::onVisibilityResolved(
+ const Transform& planeTransform) {
+}
+void LayerBuffer::Source::postBuffer(ssize_t offset) {
+}
+void LayerBuffer::Source::unregisterBuffers() {
+}
+bool LayerBuffer::Source::transformed() const {
+ return mLayer.mTransformed;
+}
+
+// ---------------------------------------------------------------------------
+
+LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
+ const ISurface::BufferHeap& buffers)
+ : Source(layer), mStatus(NO_ERROR), mBufferSize(0),
+ mUseEGLImageDirectly(true)
+{
+ if (buffers.heap == NULL) {
+ // this is allowed, but in this case, it is illegal to receive
+ // postBuffer(). The surface just erases the framebuffer with
+ // fully transparent pixels.
+ mBufferHeap = buffers;
+ mLayer.setNeedsBlending(false);
+ return;
+ }
+
+ status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT;
+ if (err != NO_ERROR) {
+ LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err));
+ mStatus = err;
+ return;
+ }
+
+ PixelFormatInfo info;
+ err = getPixelFormatInfo(buffers.format, &info);
+ if (err != NO_ERROR) {
+ LOGE("LayerBuffer::BufferSource: invalid format %d (%s)",
+ buffers.format, strerror(err));
+ mStatus = err;
+ return;
+ }
+
+ if (buffers.hor_stride<0 || buffers.ver_stride<0) {
+ LOGE("LayerBuffer::BufferSource: invalid parameters "
+ "(w=%d, h=%d, xs=%d, ys=%d)",
+ buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride);
+ mStatus = BAD_VALUE;
+ return;
+ }
+
+ mBufferHeap = buffers;
+ mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0);
+ mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride;
+ mLayer.forceVisibilityTransaction();
+}
+
+LayerBuffer::BufferSource::~BufferSource()
+{
+ class MessageDestroyTexture : public MessageBase {
+ SurfaceFlinger* flinger;
+ GLuint name;
+ public:
+ MessageDestroyTexture(
+ SurfaceFlinger* flinger, GLuint name)
+ : flinger(flinger), name(name) { }
+ virtual bool handler() {
+ glDeleteTextures(1, &name);
+ return true;
+ }
+ };
+
+ if (mTexture.name != -1U) {
+ // GL textures can only be destroyed from the GL thread
+ mLayer.mFlinger->mEventQueue.postMessage(
+ new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) );
+ }
+ if (mTexture.image != EGL_NO_IMAGE_KHR) {
+ EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ eglDestroyImageKHR(dpy, mTexture.image);
+ }
+}
+
+void LayerBuffer::BufferSource::postBuffer(ssize_t offset)
+{
+ ISurface::BufferHeap buffers;
+ { // scope for the lock
+ Mutex::Autolock _l(mBufferSourceLock);
+ buffers = mBufferHeap;
+ if (buffers.heap != 0) {
+ const size_t memorySize = buffers.heap->getSize();
+ if ((size_t(offset) + mBufferSize) > memorySize) {
+ LOGE("LayerBuffer::BufferSource::postBuffer() "
+ "invalid buffer (offset=%d, size=%d, heap-size=%d",
+ int(offset), int(mBufferSize), int(memorySize));
+ return;
+ }
+ }
+ }
+
+ sp<Buffer> buffer;
+ if (buffers.heap != 0) {
+ buffer = new LayerBuffer::Buffer(buffers, offset, mBufferSize);
+ if (buffer->getStatus() != NO_ERROR)
+ buffer.clear();
+ setBuffer(buffer);
+ mLayer.invalidate();
+ }
+}
+
+void LayerBuffer::BufferSource::unregisterBuffers()
+{
+ Mutex::Autolock _l(mBufferSourceLock);
+ mBufferHeap.heap.clear();
+ mBuffer.clear();
+ mLayer.invalidate();
+}
+
+sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const
+{
+ Mutex::Autolock _l(mBufferSourceLock);
+ return mBuffer;
+}
+
+void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
+{
+ Mutex::Autolock _l(mBufferSourceLock);
+ mBuffer = buffer;
+}
+
+bool LayerBuffer::BufferSource::transformed() const
+{
+ return mBufferHeap.transform ? true : Source::transformed();
+}
+
+void LayerBuffer::BufferSource::onDraw(const Region& clip) const
+{
+ sp<Buffer> ourBuffer(getBuffer());
+ if (UNLIKELY(ourBuffer == 0)) {
+ // nothing to do, we don't have a buffer
+ mLayer.clearWithOpenGL(clip);
+ return;
+ }
+
+ status_t err = NO_ERROR;
+ NativeBuffer src(ourBuffer->getBuffer());
+ const Rect transformedBounds(mLayer.getTransformedBounds());
+
+ if (UNLIKELY(mTexture.name == -1LU)) {
+ mTexture.name = mLayer.createTexture();
+ }
+
+#if defined(EGL_ANDROID_image_native_buffer)
+ if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
+ err = INVALID_OPERATION;
+ if (ourBuffer->supportsCopybit()) {
+
+ // there are constraints on buffers used by the GPU and these may not
+ // be honored here. We need to change the API so the buffers
+ // are allocated with gralloc. For now disable this code-path
+#if 0
+ // First, try to use the buffer as an EGLImage directly
+ if (mUseEGLImageDirectly) {
+ // NOTE: Assume the buffer is allocated with the proper USAGE flags
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ src.img.w, src.img.h, src.img.format,
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ src.img.w, src.img.handle, false);
+
+ err = mLayer.initializeEglImage(buffer, &mTexture);
+ if (err != NO_ERROR) {
+ mUseEGLImageDirectly = false;
+ }
+ }
+#endif
+
+ copybit_device_t* copybit = mLayer.mBlitEngine;
+ if (copybit && err != NO_ERROR) {
+ // create our EGLImageKHR the first time
+ err = initTempBuffer();
+ if (err == NO_ERROR) {
+ // NOTE: Assume the buffer is allocated with the proper USAGE flags
+ const NativeBuffer& dst(mTempBuffer);
+ region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
+ err = copybit->stretch(copybit, &dst.img, &src.img,
+ &dst.crop, &src.crop, &clip);
+ if (err != NO_ERROR) {
+ clearTempBufferImage();
+ }
+ }
+ }
+ }
+ }
+#endif
+ else {
+ err = INVALID_OPERATION;
+ }
+
+ if (err != NO_ERROR) {
+ // slower fallback
+ GGLSurface t;
+ t.version = sizeof(GGLSurface);
+ t.width = src.crop.r;
+ t.height = src.crop.b;
+ t.stride = src.img.w;
+ t.vstride= src.img.h;
+ t.format = src.img.format;
+ t.data = (GGLubyte*)src.img.base;
+ const Region dirty(Rect(t.width, t.height));
+ mLayer.loadTexture(&mTexture, dirty, t);
+ }
+
+ mTexture.transform = mBufferHeap.transform;
+ mLayer.drawWithOpenGL(clip, mTexture);
+}
+
+status_t LayerBuffer::BufferSource::initTempBuffer() const
+{
+ // figure out the size we need now
+ const ISurface::BufferHeap& buffers(mBufferHeap);
+ uint32_t w = mLayer.mTransformedBounds.width();
+ uint32_t h = mLayer.mTransformedBounds.height();
+ if (buffers.w * h != buffers.h * w) {
+ int t = w; w = h; h = t;
+ }
+
+ // we're in the copybit case, so make sure we can handle this blit
+ // we don't have to keep the aspect ratio here
+ copybit_device_t* copybit = mLayer.mBlitEngine;
+ const int down = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT);
+ const int up = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT);
+ if (buffers.w > w*down) w = buffers.w / down;
+ else if (w > buffers.w*up) w = buffers.w*up;
+ if (buffers.h > h*down) h = buffers.h / down;
+ else if (h > buffers.h*up) h = buffers.h*up;
+
+ if (mTexture.image != EGL_NO_IMAGE_KHR) {
+ // we have an EGLImage, make sure the needed size didn't change
+ if (w!=mTexture.width || h!= mTexture.height) {
+ // delete the EGLImage and texture
+ clearTempBufferImage();
+ } else {
+ // we're good, we have an EGLImageKHR and it's (still) the
+ // right size
+ return NO_ERROR;
+ }
+ }
+
+ // figure out if we need linear filtering
+ if (buffers.w * h == buffers.h * w) {
+ // same pixel area, don't use filtering
+ mLayer.mUseLinearFiltering = false;
+ }
+
+ // Allocate a temporary buffer and create the corresponding EGLImageKHR
+ // once the EGLImage has been created we don't need the
+ // graphic buffer reference anymore.
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ w, h, HAL_PIXEL_FORMAT_RGB_565,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_HW_2D);
+
+ status_t err = buffer->initCheck();
+ if (err == NO_ERROR) {
+ NativeBuffer& dst(mTempBuffer);
+ dst.img.w = buffer->getStride();
+ dst.img.h = h;
+ dst.img.format = buffer->getPixelFormat();
+ dst.img.handle = (native_handle_t *)buffer->handle;
+ dst.img.base = 0;
+ dst.crop.l = 0;
+ dst.crop.t = 0;
+ dst.crop.r = w;
+ dst.crop.b = h;
+
+ err = mLayer.initializeEglImage(buffer, &mTexture);
+ }
+
+ return err;
+}
+
+void LayerBuffer::BufferSource::clearTempBufferImage() const
+{
+ // delete the image
+ EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ eglDestroyImageKHR(dpy, mTexture.image);
+
+ // and the associated texture (recreate a name)
+ glDeleteTextures(1, &mTexture.name);
+ Texture defaultTexture;
+ mTexture = defaultTexture;
+ mTexture.name = mLayer.createTexture();
+}
+
+// ---------------------------------------------------------------------------
+
+LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
+ sp<OverlayRef>* overlayRef,
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation)
+ : Source(layer), mVisibilityChanged(false),
+ mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation)
+{
+ overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
+ if (overlay_dev == NULL) {
+ // overlays not supported
+ return;
+ }
+
+ mOverlayDevice = overlay_dev;
+ overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format);
+ if (overlay == NULL) {
+ // couldn't create the overlay (no memory? no more overlays?)
+ return;
+ }
+
+ // enable dithering...
+ overlay_dev->setParameter(overlay_dev, overlay,
+ OVERLAY_DITHER, OVERLAY_ENABLE);
+
+ mOverlay = overlay;
+ mWidth = overlay->w;
+ mHeight = overlay->h;
+ mFormat = overlay->format;
+ mWidthStride = overlay->w_stride;
+ mHeightStride = overlay->h_stride;
+ mInitialized = false;
+
+ mOverlayHandle = overlay->getHandleRef(overlay);
+
+ sp<OverlayChannel> channel = new OverlayChannel( &layer );
+
+ *overlayRef = new OverlayRef(mOverlayHandle, channel,
+ mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
+ mLayer.mFlinger->signalEvent();
+}
+
+LayerBuffer::OverlaySource::~OverlaySource()
+{
+ if (mOverlay && mOverlayDevice) {
+ overlay_control_device_t* overlay_dev = mOverlayDevice;
+ overlay_dev->destroyOverlay(overlay_dev, mOverlay);
+ }
+}
+
+void LayerBuffer::OverlaySource::onDraw(const Region& clip) const
+{
+ // this would be where the color-key would be set, should we need it.
+ GLclampx red = 0;
+ GLclampx green = 0;
+ GLclampx blue = 0;
+ mLayer.clearWithOpenGL(clip, red, green, blue, 0);
+}
+
+void LayerBuffer::OverlaySource::onTransaction(uint32_t flags)
+{
+ const Layer::State& front(mLayer.drawingState());
+ const Layer::State& temp(mLayer.currentState());
+ if (temp.sequence != front.sequence) {
+ mVisibilityChanged = true;
+ }
+}
+
+void LayerBuffer::OverlaySource::onVisibilityResolved(
+ const Transform& planeTransform)
+{
+ // this code-path must be as tight as possible, it's called each time
+ // the screen is composited.
+ if (UNLIKELY(mOverlay != 0)) {
+ if (mVisibilityChanged || !mInitialized) {
+ mVisibilityChanged = false;
+ mInitialized = true;
+ const Rect bounds(mLayer.getTransformedBounds());
+ int x = bounds.left;
+ int y = bounds.top;
+ int w = bounds.width();
+ int h = bounds.height();
+
+ // we need a lock here to protect "destroy"
+ Mutex::Autolock _l(mOverlaySourceLock);
+ if (mOverlay) {
+ overlay_control_device_t* overlay_dev = mOverlayDevice;
+ overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h);
+ // we need to combine the layer orientation and the
+ // user-requested orientation.
+ Transform finalTransform = Transform(mOrientation) *
+ Transform(mLayer.getOrientation());
+ overlay_dev->setParameter(overlay_dev, mOverlay,
+ OVERLAY_TRANSFORM, finalTransform.getOrientation());
+ overlay_dev->commit(overlay_dev, mOverlay);
+ }
+ }
+ }
+}
+
+void LayerBuffer::OverlaySource::destroy()
+{
+ // we need a lock here to protect "onVisibilityResolved"
+ Mutex::Autolock _l(mOverlaySourceLock);
+ if (mOverlay && mOverlayDevice) {
+ overlay_control_device_t* overlay_dev = mOverlayDevice;
+ overlay_dev->destroyOverlay(overlay_dev, mOverlay);
+ mOverlay = 0;
+ }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h
new file mode 100644
index 0000000..b176623
--- /dev/null
+++ b/services/surfaceflinger/LayerBuffer.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_BUFFER_H
+#define ANDROID_LAYER_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "LayerBase.h"
+
+struct copybit_device_t;
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class Buffer;
+class Region;
+class OverlayRef;
+
+// ---------------------------------------------------------------------------
+
+class LayerBuffer : public LayerBaseClient
+{
+ class Source : public LightRefBase<Source> {
+ public:
+ Source(LayerBuffer& layer);
+ virtual ~Source();
+ virtual void onDraw(const Region& clip) const;
+ virtual void onTransaction(uint32_t flags);
+ virtual void onVisibilityResolved(const Transform& planeTransform);
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+ virtual bool transformed() const;
+ virtual void destroy() { }
+ protected:
+ LayerBuffer& mLayer;
+ };
+
+public:
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i);
+ virtual ~LayerBuffer();
+
+ virtual void onFirstRef();
+ virtual bool needsBlending() const;
+
+ virtual sp<LayerBaseClient::Surface> createSurface() const;
+ virtual status_t ditch();
+ virtual void onDraw(const Region& clip) const;
+ virtual uint32_t doTransaction(uint32_t flags);
+ virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
+ virtual bool transformed() const;
+
+ status_t registerBuffers(const ISurface::BufferHeap& buffers);
+ void postBuffer(ssize_t offset);
+ void unregisterBuffers();
+ sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format,
+ int32_t orientation);
+
+ sp<Source> getSource() const;
+ sp<Source> clearSource();
+ void setNeedsBlending(bool blending);
+ Rect getTransformedBounds() const {
+ return mTransformedBounds;
+ }
+
+ void serverDestroy();
+
+private:
+ struct NativeBuffer {
+ copybit_image_t img;
+ copybit_rect_t crop;
+ };
+
+ static gralloc_module_t const* sGrallocModule;
+ static gralloc_module_t const* getGrallocModule() {
+ return sGrallocModule;
+ }
+
+ class Buffer : public LightRefBase<Buffer> {
+ public:
+ Buffer(const ISurface::BufferHeap& buffers,
+ ssize_t offset, size_t bufferSize);
+ inline bool supportsCopybit() const {
+ return mSupportsCopybit;
+ }
+ inline status_t getStatus() const {
+ return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT;
+ }
+ inline const NativeBuffer& getBuffer() const {
+ return mNativeBuffer;
+ }
+ protected:
+ friend class LightRefBase<Buffer>;
+ Buffer& operator = (const Buffer& rhs);
+ Buffer(const Buffer& rhs);
+ ~Buffer();
+ private:
+ ISurface::BufferHeap mBufferHeap;
+ NativeBuffer mNativeBuffer;
+ bool mSupportsCopybit;
+ };
+
+ class BufferSource : public Source {
+ public:
+ BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers);
+ virtual ~BufferSource();
+
+ status_t getStatus() const { return mStatus; }
+ sp<Buffer> getBuffer() const;
+ void setBuffer(const sp<Buffer>& buffer);
+
+ virtual void onDraw(const Region& clip) const;
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+ virtual bool transformed() const;
+ virtual void destroy() { }
+ private:
+ status_t initTempBuffer() const;
+ void clearTempBufferImage() const;
+ mutable Mutex mBufferSourceLock;
+ sp<Buffer> mBuffer;
+ status_t mStatus;
+ ISurface::BufferHeap mBufferHeap;
+ size_t mBufferSize;
+ mutable LayerBase::Texture mTexture;
+ mutable NativeBuffer mTempBuffer;
+ mutable bool mUseEGLImageDirectly;
+ };
+
+ class OverlaySource : public Source {
+ public:
+ OverlaySource(LayerBuffer& layer,
+ sp<OverlayRef>* overlayRef,
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
+ virtual ~OverlaySource();
+ virtual void onDraw(const Region& clip) const;
+ virtual void onTransaction(uint32_t flags);
+ virtual void onVisibilityResolved(const Transform& planeTransform);
+ virtual void destroy();
+ private:
+
+ class OverlayChannel : public BnOverlay {
+ wp<LayerBuffer> mLayer;
+ virtual void destroy() {
+ sp<LayerBuffer> layer(mLayer.promote());
+ if (layer != 0) {
+ layer->serverDestroy();
+ }
+ }
+ public:
+ OverlayChannel(const sp<LayerBuffer>& layer)
+ : mLayer(layer) {
+ }
+ };
+
+ friend class OverlayChannel;
+ bool mVisibilityChanged;
+
+ overlay_t* mOverlay;
+ overlay_handle_t mOverlayHandle;
+ overlay_control_device_t* mOverlayDevice;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ int32_t mFormat;
+ int32_t mWidthStride;
+ int32_t mHeightStride;
+ int32_t mOrientation;
+ mutable Mutex mOverlaySourceLock;
+ bool mInitialized;
+ };
+
+
+ class SurfaceLayerBuffer : public LayerBaseClient::Surface
+ {
+ public:
+ SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<LayerBuffer>& owner);
+ virtual ~SurfaceLayerBuffer();
+
+ virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+
+ virtual sp<OverlayRef> createOverlay(
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
+ private:
+ sp<LayerBuffer> getOwner() const {
+ return static_cast<LayerBuffer*>(Surface::getOwner().get());
+ }
+ };
+
+ mutable Mutex mLock;
+ sp<Source> mSource;
+ sp<Surface> mSurface;
+ bool mInvalidate;
+ bool mNeedsBlending;
+ copybit_device_t* mBlitEngine;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_BUFFER_H
diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp
new file mode 100644
index 0000000..fd61e30
--- /dev/null
+++ b/services/surfaceflinger/LayerDim.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "LayerDim.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10;
+const char* const LayerDim::typeID = "LayerDim";
+
+bool LayerDim::sUseTexture;
+GLuint LayerDim::sTexId;
+EGLImageKHR LayerDim::sImage;
+int32_t LayerDim::sWidth;
+int32_t LayerDim::sHeight;
+
+// ---------------------------------------------------------------------------
+
+LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i)
+ : LayerBaseClient(flinger, display, client, i)
+{
+}
+
+void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h)
+{
+ sTexId = -1;
+ sImage = EGL_NO_IMAGE_KHR;
+ sWidth = w;
+ sHeight = h;
+ sUseTexture = false;
+
+#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
+
+#warning "using a texture to implement LayerDim"
+
+ /* On some h/w like msm7K, it is faster to use a texture because the
+ * software renderer will defer to copybit, for this to work we need to
+ * use an EGLImage texture so copybit can actually make use of it.
+ * This burns a full-screen worth of graphic memory.
+ */
+
+ const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
+ uint32_t flags = hw.getFlags();
+
+ if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565,
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+ GraphicBuffer::USAGE_HW_TEXTURE);
+
+ android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+
+ glGenTextures(1, &sTexId);
+ glBindTexture(GL_TEXTURE_2D, sTexId);
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0);
+ if (sImage == EGL_NO_IMAGE_KHR) {
+ LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
+ return;
+ }
+
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage);
+ GLint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ eglDestroyImageKHR(dpy, sImage);
+ LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error);
+ return;
+ }
+
+ // initialize the texture with zeros
+ GGLSurface t;
+ buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ memset(t.data, 0, t.stride * t.height * 2);
+ buffer->unlock();
+ sUseTexture = true;
+ }
+#endif
+}
+
+LayerDim::~LayerDim()
+{
+}
+
+void LayerDim::onDraw(const Region& clip) const
+{
+ const State& s(drawingState());
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (s.alpha>0 && (it != end)) {
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const GGLfixed alpha = (s.alpha << 16)/255;
+ const uint32_t fbHeight = hw.getHeight();
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4x(0, 0, 0, alpha);
+
+#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer)
+ if (sUseTexture) {
+ glBindTexture(GL_TEXTURE_2D, sTexId);
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ const GLshort texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { 1, 0 }
+ };
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_SHORT, 0, texCoords);
+ } else
+#endif
+ {
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ GLshort w = sWidth;
+ GLshort h = sHeight;
+ const GLshort vertices[4][2] = {
+ { 0, 0 },
+ { 0, h },
+ { w, h },
+ { w, 0 }
+ };
+ glVertexPointer(2, GL_SHORT, 0, vertices);
+
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h
new file mode 100644
index 0000000..d4672a1
--- /dev/null
+++ b/services/surfaceflinger/LayerDim.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_DIM_H
+#define ANDROID_LAYER_DIM_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "LayerBase.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class LayerDim : public LayerBaseClient
+{
+ static bool sUseTexture;
+ static GLuint sTexId;
+ static EGLImageKHR sImage;
+ static int32_t sWidth;
+ static int32_t sHeight;
+public:
+ static const uint32_t typeInfo;
+ static const char* const typeID;
+ virtual char const* getTypeID() const { return typeID; }
+ virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ LayerDim(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& client, int32_t i);
+ virtual ~LayerDim();
+
+ virtual void onDraw(const Region& clip) const;
+ virtual bool needsBlending() const { return true; }
+ virtual bool isSecure() const { return false; }
+
+ static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h);
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_DIM_H
diff --git a/services/surfaceflinger/MODULE_LICENSE_APACHE2 b/services/surfaceflinger/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/surfaceflinger/MODULE_LICENSE_APACHE2
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
new file mode 100644
index 0000000..b43d801
--- /dev/null
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+
+#include "MessageQueue.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+void MessageList::insert(const sp<MessageBase>& node)
+{
+ LIST::iterator cur(mList.begin());
+ LIST::iterator end(mList.end());
+ while (cur != end) {
+ if (*node < **cur) {
+ mList.insert(cur, node);
+ return;
+ }
+ ++cur;
+ }
+ mList.insert(++end, node);
+}
+
+void MessageList::remove(MessageList::LIST::iterator pos)
+{
+ mList.erase(pos);
+}
+
+// ---------------------------------------------------------------------------
+
+MessageQueue::MessageQueue()
+ : mInvalidate(false)
+{
+ mInvalidateMessage = new MessageBase(INVALIDATE);
+}
+
+MessageQueue::~MessageQueue()
+{
+}
+
+MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
+{
+ MessageList::value_type result;
+
+ bool again;
+ do {
+ const nsecs_t timeoutTime = systemTime() + timeout;
+ while (true) {
+ Mutex::Autolock _l(mLock);
+ nsecs_t now = systemTime();
+ nsecs_t nextEventTime = -1;
+
+ // invalidate messages are always handled first
+ if (mInvalidate) {
+ mInvalidate = false;
+ mInvalidateMessage->when = now;
+ result = mInvalidateMessage;
+ break;
+ }
+
+ LIST::iterator cur(mMessages.begin());
+ if (cur != mMessages.end()) {
+ result = *cur;
+ }
+
+ if (result != 0) {
+ if (result->when <= now) {
+ // there is a message to deliver
+ mMessages.remove(cur);
+ break;
+ }
+ if (timeout>=0 && timeoutTime < now) {
+ // we timed-out, return a NULL message
+ result = 0;
+ break;
+ }
+ nextEventTime = result->when;
+ result = 0;
+ }
+
+ if (timeout >= 0 && nextEventTime > 0) {
+ if (nextEventTime > timeoutTime) {
+ nextEventTime = timeoutTime;
+ }
+ }
+
+ if (nextEventTime >= 0) {
+ //LOGD("nextEventTime = %lld ms", nextEventTime);
+ if (nextEventTime > 0) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ const nsecs_t reltime = nextEventTime - systemTime();
+ if (reltime > 0) {
+ mCondition.waitRelative(mLock, reltime);
+ }
+ }
+ } else {
+ //LOGD("going to wait");
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ mCondition.wait(mLock);
+ }
+ }
+ // here we're not holding the lock anymore
+
+ if (result == 0)
+ break;
+
+ again = result->handler();
+ if (again) {
+ // the message has been processed. release our reference to it
+ // without holding the lock.
+ result = 0;
+ }
+
+ } while (again);
+
+ return result;
+}
+
+status_t MessageQueue::postMessage(
+ const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+{
+ return queueMessage(message, relTime, flags);
+}
+
+status_t MessageQueue::invalidate() {
+ Mutex::Autolock _l(mLock);
+ mInvalidate = true;
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+status_t MessageQueue::queueMessage(
+ const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+{
+ Mutex::Autolock _l(mLock);
+ message->when = systemTime() + relTime;
+ mMessages.insert(message);
+
+ //LOGD("MessageQueue::queueMessage time = %lld ms", message->when);
+ //dumpLocked(message);
+
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+void MessageQueue::dump(const MessageList::value_type& message)
+{
+ Mutex::Autolock _l(mLock);
+ dumpLocked(message);
+}
+
+void MessageQueue::dumpLocked(const MessageList::value_type& message)
+{
+ LIST::const_iterator cur(mMessages.begin());
+ LIST::const_iterator end(mMessages.end());
+ int c = 0;
+ while (cur != end) {
+ const char tick = (*cur == message) ? '>' : ' ';
+ LOGD("%c %d: msg{.what=%08x, when=%lld}",
+ tick, c, (*cur)->what, (*cur)->when);
+ ++cur;
+ c++;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h
new file mode 100644
index 0000000..dc8138d
--- /dev/null
+++ b/services/surfaceflinger/MessageQueue.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MESSAGE_QUEUE_H
+#define ANDROID_MESSAGE_QUEUE_H
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/List.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class MessageBase;
+
+class MessageList
+{
+ List< sp<MessageBase> > mList;
+ typedef List< sp<MessageBase> > LIST;
+public:
+ typedef sp<MessageBase> value_type;
+ inline LIST::iterator begin() { return mList.begin(); }
+ inline LIST::const_iterator begin() const { return mList.begin(); }
+ inline LIST::iterator end() { return mList.end(); }
+ inline LIST::const_iterator end() const { return mList.end(); }
+ inline bool isEmpty() const { return mList.empty(); }
+ void insert(const sp<MessageBase>& node);
+ void remove(LIST::iterator pos);
+};
+
+// ============================================================================
+
+class MessageBase :
+ public LightRefBase<MessageBase>
+{
+public:
+ nsecs_t when;
+ uint32_t what;
+ int32_t arg0;
+
+ MessageBase() : when(0), what(0), arg0(0) { }
+ MessageBase(uint32_t what, int32_t arg0=0)
+ : when(0), what(what), arg0(arg0) { }
+
+ // return true if message has a handler
+ virtual bool handler() { return false; }
+
+protected:
+ virtual ~MessageBase() { }
+
+private:
+ friend class LightRefBase<MessageBase>;
+};
+
+inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
+ return lhs.when < rhs.when;
+}
+
+// ---------------------------------------------------------------------------
+
+class MessageQueue
+{
+ typedef List< sp<MessageBase> > LIST;
+public:
+
+ // this is a work-around the multichar constant warning. A macro would
+ // work too, but would pollute the namespace.
+ template <int a, int b, int c, int d>
+ struct WHAT {
+ static const uint32_t Value =
+ (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)|
+ (uint32_t(c&0xff)<<8)|uint32_t(d&0xff);
+ };
+
+ MessageQueue();
+ ~MessageQueue();
+
+ // pre-defined messages
+ enum {
+ INVALIDATE = WHAT<'_','p','d','t'>::Value
+ };
+
+ MessageList::value_type waitMessage(nsecs_t timeout = -1);
+
+ status_t postMessage(const MessageList::value_type& message,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
+ status_t invalidate();
+
+ void dump(const MessageList::value_type& message);
+
+private:
+ status_t queueMessage(const MessageList::value_type& message,
+ nsecs_t reltime, uint32_t flags);
+ void dumpLocked(const MessageList::value_type& message);
+
+ Mutex mLock;
+ Condition mCondition;
+ MessageList mMessages;
+ bool mInvalidate;
+ MessageList::value_type mInvalidateMessage;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
new file mode 100644
index 0000000..0722fda
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -0,0 +1,1940 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/StopWatch.h>
+
+#include <ui/GraphicBufferAllocator.h>
+#include <ui/PixelFormat.h>
+
+#include <pixelflinger/pixelflinger.h>
+#include <GLES/gl.h>
+
+#include "clz.h"
+#include "Layer.h"
+#include "LayerBlur.h"
+#include "LayerBuffer.h"
+#include "LayerDim.h"
+#include "SurfaceFlinger.h"
+
+#include "DisplayHardware/DisplayHardware.h"
+
+/* ideally AID_GRAPHICS would be in a semi-public header
+ * or there would be a way to map a user/group name to its id
+ */
+#ifndef AID_GRAPHICS
+#define AID_GRAPHICS 1003
+#endif
+
+#define DISPLAY_COUNT 1
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+void SurfaceFlinger::instantiate() {
+ defaultServiceManager()->addService(
+ String16("SurfaceFlinger"), new SurfaceFlinger());
+}
+
+void SurfaceFlinger::shutdown() {
+ // we should unregister here, but not really because
+ // when (if) the service manager goes away, all the services
+ // it has a reference to will leave too.
+}
+
+// ---------------------------------------------------------------------------
+
+SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
+ : lookup(rhs.lookup), layers(rhs.layers)
+{
+}
+
+ssize_t SurfaceFlinger::LayerVector::indexOf(
+ const sp<LayerBase>& key, size_t guess) const
+{
+ if (guess<size() && lookup.keyAt(guess) == key)
+ return guess;
+ const ssize_t i = lookup.indexOfKey(key);
+ if (i>=0) {
+ const size_t idx = lookup.valueAt(i);
+ LOGE_IF(layers[idx]!=key,
+ "LayerVector[%p]: layers[%d]=%p, key=%p",
+ this, int(idx), layers[idx].get(), key.get());
+ return idx;
+ }
+ return i;
+}
+
+ssize_t SurfaceFlinger::LayerVector::add(
+ const sp<LayerBase>& layer,
+ Vector< sp<LayerBase> >::compar_t cmp)
+{
+ size_t count = layers.size();
+ ssize_t l = 0;
+ ssize_t h = count-1;
+ ssize_t mid;
+ sp<LayerBase> const* a = layers.array();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const int c = cmp(a+mid, &layer);
+ if (c == 0) { l = mid; break; }
+ else if (c<0) { l = mid+1; }
+ else { h = mid-1; }
+ }
+ size_t order = l;
+ while (order<count && !cmp(&layer, a+order)) {
+ order++;
+ }
+ count = lookup.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (lookup.valueAt(i) >= order) {
+ lookup.editValueAt(i)++;
+ }
+ }
+ layers.insertAt(layer, order);
+ lookup.add(layer, order);
+ return order;
+}
+
+ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
+{
+ const ssize_t keyIndex = lookup.indexOfKey(layer);
+ if (keyIndex >= 0) {
+ const size_t index = lookup.valueAt(keyIndex);
+ LOGE_IF(layers[index]!=layer,
+ "LayerVector[%p]: layers[%u]=%p, layer=%p",
+ this, int(index), layers[index].get(), layer.get());
+ layers.removeItemsAt(index);
+ lookup.removeItemsAt(keyIndex);
+ const size_t count = lookup.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (lookup.valueAt(i) >= size_t(index)) {
+ lookup.editValueAt(i)--;
+ }
+ }
+ return index;
+ }
+ return NAME_NOT_FOUND;
+}
+
+ssize_t SurfaceFlinger::LayerVector::reorder(
+ const sp<LayerBase>& layer,
+ Vector< sp<LayerBase> >::compar_t cmp)
+{
+ // XXX: it's a little lame. but oh well...
+ ssize_t err = remove(layer);
+ if (err >=0)
+ err = add(layer, cmp);
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+SurfaceFlinger::SurfaceFlinger()
+ : BnSurfaceComposer(), Thread(false),
+ mTransactionFlags(0),
+ mTransactionCount(0),
+ mResizeTransationPending(false),
+ mLayersRemoved(false),
+ mBootTime(systemTime()),
+ mHardwareTest("android.permission.HARDWARE_TEST"),
+ mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+ mDump("android.permission.DUMP"),
+ mVisibleRegionsDirty(false),
+ mDeferReleaseConsole(false),
+ mFreezeDisplay(false),
+ mFreezeCount(0),
+ mFreezeDisplayTime(0),
+ mDebugRegion(0),
+ mDebugBackground(0),
+ mDebugInSwapBuffers(0),
+ mLastSwapBufferTime(0),
+ mDebugInTransaction(0),
+ mLastTransactionTime(0),
+ mBootFinished(false),
+ mConsoleSignals(0),
+ mSecureFrameBuffer(0)
+{
+ init();
+}
+
+void SurfaceFlinger::init()
+{
+ LOGI("SurfaceFlinger is starting");
+
+ // debugging stuff...
+ char value[PROPERTY_VALUE_MAX];
+ property_get("debug.sf.showupdates", value, "0");
+ mDebugRegion = atoi(value);
+ property_get("debug.sf.showbackground", value, "0");
+ mDebugBackground = atoi(value);
+
+ LOGI_IF(mDebugRegion, "showupdates enabled");
+ LOGI_IF(mDebugBackground, "showbackground enabled");
+}
+
+SurfaceFlinger::~SurfaceFlinger()
+{
+ glDeleteTextures(1, &mWormholeTexName);
+}
+
+overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const
+{
+ return graphicPlane(0).displayHardware().getOverlayEngine();
+}
+
+sp<IMemoryHeap> SurfaceFlinger::getCblk() const
+{
+ return mServerHeap;
+}
+
+sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
+{
+ Mutex::Autolock _l(mStateLock);
+ uint32_t token = mTokens.acquire();
+
+ sp<Client> client = new Client(token, this);
+ if (client->ctrlblk == 0) {
+ mTokens.release(token);
+ return 0;
+ }
+ status_t err = mClientsMap.add(token, client);
+ if (err < 0) {
+ mTokens.release(token);
+ return 0;
+ }
+ sp<BClient> bclient =
+ new BClient(this, token, client->getControlBlockMemory());
+ return bclient;
+}
+
+void SurfaceFlinger::destroyConnection(ClientID cid)
+{
+ Mutex::Autolock _l(mStateLock);
+ sp<Client> client = mClientsMap.valueFor(cid);
+ if (client != 0) {
+ // free all the layers this client owns
+ Vector< wp<LayerBaseClient> > layers(client->getLayers());
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<LayerBaseClient> layer(layers[i].promote());
+ if (layer != 0) {
+ purgatorizeLayer_l(layer);
+ }
+ }
+
+ // the resources associated with this client will be freed
+ // during the next transaction, after these surfaces have been
+ // properly removed from the screen
+
+ // remove this client from our ClientID->Client mapping.
+ mClientsMap.removeItem(cid);
+
+ // and add it to the list of disconnected clients
+ mDisconnectedClients.add(client);
+
+ // request a transaction
+ setTransactionFlags(eTransactionNeeded);
+ }
+}
+
+const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const
+{
+ LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy);
+ const GraphicPlane& plane(mGraphicPlanes[dpy]);
+ return plane;
+}
+
+GraphicPlane& SurfaceFlinger::graphicPlane(int dpy)
+{
+ return const_cast<GraphicPlane&>(
+ const_cast<SurfaceFlinger const *>(this)->graphicPlane(dpy));
+}
+
+void SurfaceFlinger::bootFinished()
+{
+ const nsecs_t now = systemTime();
+ const nsecs_t duration = now - mBootTime;
+ LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
+ mBootFinished = true;
+ property_set("ctl.stop", "bootanim");
+}
+
+void SurfaceFlinger::onFirstRef()
+{
+ run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
+
+ // Wait for the main thread to be done with its initialization
+ mReadyToRunBarrier.wait();
+}
+
+static inline uint16_t pack565(int r, int g, int b) {
+ return (r<<11)|(g<<5)|b;
+}
+
+status_t SurfaceFlinger::readyToRun()
+{
+ LOGI( "SurfaceFlinger's main thread ready to run. "
+ "Initializing graphics H/W...");
+
+ // we only support one display currently
+ int dpy = 0;
+
+ {
+ // initialize the main display
+ GraphicPlane& plane(graphicPlane(dpy));
+ DisplayHardware* const hw = new DisplayHardware(this, dpy);
+ plane.setDisplayHardware(hw);
+ }
+
+ // create the shared control-block
+ mServerHeap = new MemoryHeapBase(4096,
+ MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
+ LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
+
+ mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
+ LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
+
+ new(mServerCblk) surface_flinger_cblk_t;
+
+ // initialize primary screen
+ // (other display should be initialized in the same manner, but
+ // asynchronously, as they could come and go. None of this is supported
+ // yet).
+ const GraphicPlane& plane(graphicPlane(dpy));
+ const DisplayHardware& hw = plane.displayHardware();
+ const uint32_t w = hw.getWidth();
+ const uint32_t h = hw.getHeight();
+ const uint32_t f = hw.getFormat();
+ hw.makeCurrent();
+
+ // initialize the shared control block
+ mServerCblk->connected |= 1<<dpy;
+ display_cblk_t* dcblk = mServerCblk->displays + dpy;
+ memset(dcblk, 0, sizeof(display_cblk_t));
+ dcblk->w = plane.getWidth();
+ dcblk->h = plane.getHeight();
+ dcblk->format = f;
+ dcblk->orientation = ISurfaceComposer::eOrientationDefault;
+ dcblk->xdpi = hw.getDpiX();
+ dcblk->ydpi = hw.getDpiY();
+ dcblk->fps = hw.getRefreshRate();
+ dcblk->density = hw.getDensity();
+ asm volatile ("":::"memory");
+
+ // Initialize OpenGL|ES
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnable(GL_SCISSOR_TEST);
+ glShadeModel(GL_FLAT);
+ glDisable(GL_DITHER);
+ glDisable(GL_CULL_FACE);
+
+ const uint16_t g0 = pack565(0x0F,0x1F,0x0F);
+ const uint16_t g1 = pack565(0x17,0x2f,0x17);
+ const uint16_t textureData[4] = { g0, g1, g1, g0 };
+ glGenTextures(1, &mWormholeTexName);
+ glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData);
+
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, w, h, 0, 0, 1);
+
+ LayerDim::initDimmer(this, w, h);
+
+ mReadyToRunBarrier.open();
+
+ /*
+ * We're now ready to accept clients...
+ */
+
+ // start boot animation
+ property_set("ctl.start", "bootanim");
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Events Handler
+#endif
+
+void SurfaceFlinger::waitForEvent()
+{
+ while (true) {
+ nsecs_t timeout = -1;
+ const nsecs_t freezeDisplayTimeout = ms2ns(5000);
+ if (UNLIKELY(isFrozen())) {
+ // wait 5 seconds
+ const nsecs_t now = systemTime();
+ if (mFreezeDisplayTime == 0) {
+ mFreezeDisplayTime = now;
+ }
+ nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime);
+ timeout = waitTime>0 ? waitTime : 0;
+ }
+
+ MessageList::value_type msg = mEventQueue.waitMessage(timeout);
+
+ // see if we timed out
+ if (isFrozen()) {
+ const nsecs_t now = systemTime();
+ nsecs_t frozenTime = (now - mFreezeDisplayTime);
+ if (frozenTime >= freezeDisplayTimeout) {
+ // we timed out and are still frozen
+ LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d",
+ mFreezeDisplay, mFreezeCount);
+ mFreezeDisplayTime = 0;
+ mFreezeCount = 0;
+ mFreezeDisplay = false;
+ }
+ }
+
+ if (msg != 0) {
+ switch (msg->what) {
+ case MessageQueue::INVALIDATE:
+ // invalidate message, just return to the main loop
+ return;
+ }
+ }
+ }
+}
+
+void SurfaceFlinger::signalEvent() {
+ mEventQueue.invalidate();
+}
+
+void SurfaceFlinger::signal() const {
+ // this is the IPC call
+ const_cast<SurfaceFlinger*>(this)->signalEvent();
+}
+
+void SurfaceFlinger::signalDelayedEvent(nsecs_t delay)
+{
+ mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay);
+}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark Main loop
+#endif
+
+bool SurfaceFlinger::threadLoop()
+{
+ waitForEvent();
+
+ // check for transactions
+ if (UNLIKELY(mConsoleSignals)) {
+ handleConsoleEvents();
+ }
+
+ if (LIKELY(mTransactionCount == 0)) {
+ // if we're in a global transaction, don't do anything.
+ const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+ uint32_t transactionFlags = getTransactionFlags(mask);
+ if (LIKELY(transactionFlags)) {
+ handleTransaction(transactionFlags);
+ }
+ }
+
+ // post surfaces (if needed)
+ handlePageFlip();
+
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ if (LIKELY(hw.canDraw() && !isFrozen())) {
+ // repaint the framebuffer (if needed)
+ handleRepaint();
+
+ // inform the h/w that we're done compositing
+ hw.compositionComplete();
+
+ // release the clients before we flip ('cause flip might block)
+ unlockClients();
+
+ postFramebuffer();
+ } else {
+ // pretend we did the post
+ unlockClients();
+ usleep(16667); // 60 fps period
+ }
+ return true;
+}
+
+void SurfaceFlinger::postFramebuffer()
+{
+ if (!mInvalidRegion.isEmpty()) {
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const nsecs_t now = systemTime();
+ mDebugInSwapBuffers = now;
+ hw.flip(mInvalidRegion);
+ mLastSwapBufferTime = systemTime() - now;
+ mDebugInSwapBuffers = 0;
+ mInvalidRegion.clear();
+ }
+}
+
+void SurfaceFlinger::handleConsoleEvents()
+{
+ // something to do with the console
+ const DisplayHardware& hw = graphicPlane(0).displayHardware();
+
+ int what = android_atomic_and(0, &mConsoleSignals);
+ if (what & eConsoleAcquired) {
+ hw.acquireScreen();
+ }
+
+ if (mDeferReleaseConsole && hw.canDraw()) {
+ // We got the release signal before the acquire signal
+ mDeferReleaseConsole = false;
+ hw.releaseScreen();
+ }
+
+ if (what & eConsoleReleased) {
+ if (hw.canDraw()) {
+ hw.releaseScreen();
+ } else {
+ mDeferReleaseConsole = true;
+ }
+ }
+
+ mDirtyRegion.set(hw.bounds());
+}
+
+void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
+{
+ Vector< sp<LayerBase> > ditchedLayers;
+
+ { // scope for the lock
+ Mutex::Autolock _l(mStateLock);
+ const nsecs_t now = systemTime();
+ mDebugInTransaction = now;
+ handleTransactionLocked(transactionFlags, ditchedLayers);
+ mLastTransactionTime = systemTime() - now;
+ mDebugInTransaction = 0;
+ }
+
+ // do this without lock held
+ const size_t count = ditchedLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (ditchedLayers[i] != 0) {
+ //LOGD("ditching layer %p", ditchedLayers[i].get());
+ ditchedLayers[i]->ditch();
+ }
+ }
+}
+
+void SurfaceFlinger::handleTransactionLocked(
+ uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers)
+{
+ const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
+ const size_t count = currentLayers.size();
+
+ /*
+ * Traversal of the children
+ * (perform the transaction for each of them if needed)
+ */
+
+ const bool layersNeedTransaction = transactionFlags & eTraversalNeeded;
+ if (layersNeedTransaction) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer = currentLayers[i];
+ uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
+ if (!trFlags) continue;
+
+ const uint32_t flags = layer->doTransaction(0);
+ if (flags & Layer::eVisibleRegion)
+ mVisibleRegionsDirty = true;
+ }
+ }
+
+ /*
+ * Perform our own transaction if needed
+ */
+
+ if (transactionFlags & eTransactionNeeded) {
+ if (mCurrentState.orientation != mDrawingState.orientation) {
+ // the orientation has changed, recompute all visible regions
+ // and invalidate everything.
+
+ const int dpy = 0;
+ const int orientation = mCurrentState.orientation;
+ const uint32_t type = mCurrentState.orientationType;
+ GraphicPlane& plane(graphicPlane(dpy));
+ plane.setOrientation(orientation);
+
+ // update the shared control block
+ const DisplayHardware& hw(plane.displayHardware());
+ volatile display_cblk_t* dcblk = mServerCblk->displays + dpy;
+ dcblk->orientation = orientation;
+ dcblk->w = plane.getWidth();
+ dcblk->h = plane.getHeight();
+
+ mVisibleRegionsDirty = true;
+ mDirtyRegion.set(hw.bounds());
+ }
+
+ if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
+ // freezing or unfreezing the display -> trigger animation if needed
+ mFreezeDisplay = mCurrentState.freezeDisplay;
+ if (mFreezeDisplay)
+ mFreezeDisplayTime = 0;
+ }
+
+ if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
+ // layers have been added
+ mVisibleRegionsDirty = true;
+ }
+
+ // some layers might have been removed, so
+ // we need to update the regions they're exposing.
+ if (mLayersRemoved) {
+ mLayersRemoved = false;
+ mVisibleRegionsDirty = true;
+ const LayerVector& previousLayers(mDrawingState.layersSortedByZ);
+ const size_t count = previousLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(previousLayers[i]);
+ if (currentLayers.indexOf( layer ) < 0) {
+ // this layer is not visible anymore
+ ditchedLayers.add(layer);
+ mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
+ }
+ }
+ }
+
+ // get rid of all resources we don't need anymore
+ // (layers and clients)
+ free_resources_l();
+ }
+
+ commitTransaction();
+}
+
+sp<FreezeLock> SurfaceFlinger::getFreezeLock() const
+{
+ return new FreezeLock(const_cast<SurfaceFlinger *>(this));
+}
+
+void SurfaceFlinger::computeVisibleRegions(
+ LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion)
+{
+ const GraphicPlane& plane(graphicPlane(0));
+ const Transform& planeTransform(plane.transform());
+ const DisplayHardware& hw(plane.displayHardware());
+ const Region screenRegion(hw.bounds());
+
+ Region aboveOpaqueLayers;
+ Region aboveCoveredLayers;
+ Region dirty;
+
+ bool secureFrameBuffer = false;
+
+ size_t i = currentLayers.size();
+ while (i--) {
+ const sp<LayerBase>& layer = currentLayers[i];
+ layer->validateVisibility(planeTransform);
+
+ // start with the whole surface at its current location
+ const Layer::State& s(layer->drawingState());
+
+ /*
+ * opaqueRegion: area of a surface that is fully opaque.
+ */
+ Region opaqueRegion;
+
+ /*
+ * visibleRegion: area of a surface that is visible on screen
+ * and not fully transparent. This is essentially the layer's
+ * footprint minus the opaque regions above it.
+ * Areas covered by a translucent surface are considered visible.
+ */
+ Region visibleRegion;
+
+ /*
+ * coveredRegion: area of a surface that is covered by all
+ * visible regions above it (which includes the translucent areas).
+ */
+ Region coveredRegion;
+
+
+ // handle hidden surfaces by setting the visible region to empty
+ if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) {
+ const bool translucent = layer->needsBlending();
+ const Rect bounds(layer->visibleBounds());
+ visibleRegion.set(bounds);
+ visibleRegion.andSelf(screenRegion);
+ if (!visibleRegion.isEmpty()) {
+ // Remove the transparent area from the visible region
+ if (translucent) {
+ visibleRegion.subtractSelf(layer->transparentRegionScreen);
+ }
+
+ // compute the opaque region
+ const int32_t layerOrientation = layer->getOrientation();
+ if (s.alpha==255 && !translucent &&
+ ((layerOrientation & Transform::ROT_INVALID) == false)) {
+ // the opaque region is the layer's footprint
+ opaqueRegion = visibleRegion;
+ }
+ }
+ }
+
+ // Clip the covered region to the visible region
+ coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
+
+ // Update aboveCoveredLayers for next (lower) layer
+ aboveCoveredLayers.orSelf(visibleRegion);
+
+ // subtract the opaque region covered by the layers above us
+ visibleRegion.subtractSelf(aboveOpaqueLayers);
+
+ // compute this layer's dirty region
+ if (layer->contentDirty) {
+ // we need to invalidate the whole region
+ dirty = visibleRegion;
+ // as well, as the old visible region
+ dirty.orSelf(layer->visibleRegionScreen);
+ layer->contentDirty = false;
+ } else {
+ /* compute the exposed region:
+ * the exposed region consists of two components:
+ * 1) what's VISIBLE now and was COVERED before
+ * 2) what's EXPOSED now less what was EXPOSED before
+ *
+ * note that (1) is conservative, we start with the whole
+ * visible region but only keep what used to be covered by
+ * something -- which mean it may have been exposed.
+ *
+ * (2) handles areas that were not covered by anything but got
+ * exposed because of a resize.
+ */
+ const Region newExposed = visibleRegion - coveredRegion;
+ const Region oldVisibleRegion = layer->visibleRegionScreen;
+ const Region oldCoveredRegion = layer->coveredRegionScreen;
+ const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+ dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
+ }
+ dirty.subtractSelf(aboveOpaqueLayers);
+
+ // accumulate to the screen dirty region
+ dirtyRegion.orSelf(dirty);
+
+ // Update aboveOpaqueLayers for next (lower) layer
+ aboveOpaqueLayers.orSelf(opaqueRegion);
+
+ // Store the visible region is screen space
+ layer->setVisibleRegion(visibleRegion);
+ layer->setCoveredRegion(coveredRegion);
+
+ // If a secure layer is partially visible, lock-down the screen!
+ if (layer->isSecure() && !visibleRegion.isEmpty()) {
+ secureFrameBuffer = true;
+ }
+ }
+
+ // invalidate the areas where a layer was removed
+ dirtyRegion.orSelf(mDirtyRegionRemovedLayer);
+ mDirtyRegionRemovedLayer.clear();
+
+ mSecureFrameBuffer = secureFrameBuffer;
+ opaqueRegion = aboveOpaqueLayers;
+}
+
+
+void SurfaceFlinger::commitTransaction()
+{
+ mDrawingState = mCurrentState;
+ mResizeTransationPending = false;
+ mTransactionCV.broadcast();
+}
+
+void SurfaceFlinger::handlePageFlip()
+{
+ bool visibleRegions = mVisibleRegionsDirty;
+ LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ);
+ visibleRegions |= lockPageFlip(currentLayers);
+
+ const DisplayHardware& hw = graphicPlane(0).displayHardware();
+ const Region screenRegion(hw.bounds());
+ if (visibleRegions) {
+ Region opaqueRegion;
+ computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+ mWormholeRegion = screenRegion.subtract(opaqueRegion);
+ mVisibleRegionsDirty = false;
+ }
+
+ unlockPageFlip(currentLayers);
+ mDirtyRegion.andSelf(screenRegion);
+}
+
+bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
+{
+ bool recomputeVisibleRegions = false;
+ size_t count = currentLayers.size();
+ sp<LayerBase> const* layers = currentLayers.array();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer = layers[i];
+ layer->lockPageFlip(recomputeVisibleRegions);
+ }
+ return recomputeVisibleRegions;
+}
+
+void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
+{
+ const GraphicPlane& plane(graphicPlane(0));
+ const Transform& planeTransform(plane.transform());
+ size_t count = currentLayers.size();
+ sp<LayerBase> const* layers = currentLayers.array();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer = layers[i];
+ layer->unlockPageFlip(planeTransform, mDirtyRegion);
+ }
+}
+
+
+void SurfaceFlinger::handleRepaint()
+{
+ // compute the invalid region
+ mInvalidRegion.orSelf(mDirtyRegion);
+ if (mInvalidRegion.isEmpty()) {
+ // nothing to do
+ return;
+ }
+
+ if (UNLIKELY(mDebugRegion)) {
+ debugFlashRegions();
+ }
+
+ // set the frame buffer
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ uint32_t flags = hw.getFlags();
+ if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
+ (flags & DisplayHardware::BUFFER_PRESERVED))
+ {
+ // we can redraw only what's dirty, but since SWAP_RECTANGLE only
+ // takes a rectangle, we must make sure to update that whole
+ // rectangle in that case
+ if (flags & DisplayHardware::SWAP_RECTANGLE) {
+ // FIXME: we really should be able to pass a region to
+ // SWAP_RECTANGLE so that we don't have to redraw all this.
+ mDirtyRegion.set(mInvalidRegion.bounds());
+ } else {
+ // in the BUFFER_PRESERVED case, obviously, we can update only
+ // what's needed and nothing more.
+ // NOTE: this is NOT a common case, as preserving the backbuffer
+ // is costly and usually involves copying the whole update back.
+ }
+ } else {
+ if (flags & DisplayHardware::PARTIAL_UPDATES) {
+ // We need to redraw the rectangle that will be updated
+ // (pushed to the framebuffer).
+ // This is needed because PARTIAL_UPDATES only takes one
+ // rectangle instead of a region (see DisplayHardware::flip())
+ mDirtyRegion.set(mInvalidRegion.bounds());
+ } else {
+ // we need to redraw everything (the whole screen)
+ mDirtyRegion.set(hw.bounds());
+ mInvalidRegion = mDirtyRegion;
+ }
+ }
+
+ // compose all surfaces
+ composeSurfaces(mDirtyRegion);
+
+ // clear the dirty regions
+ mDirtyRegion.clear();
+}
+
+void SurfaceFlinger::composeSurfaces(const Region& dirty)
+{
+ if (UNLIKELY(!mWormholeRegion.isEmpty())) {
+ // should never happen unless the window manager has a bug
+ // draw something...
+ drawWormhole();
+ }
+ const SurfaceFlinger& flinger(*this);
+ const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
+ const size_t count = drawingLayers.size();
+ sp<LayerBase> const* const layers = drawingLayers.array();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer = layers[i];
+ const Region& visibleRegion(layer->visibleRegionScreen);
+ if (!visibleRegion.isEmpty()) {
+ const Region clip(dirty.intersect(visibleRegion));
+ if (!clip.isEmpty()) {
+ layer->draw(clip);
+ }
+ }
+ }
+}
+
+void SurfaceFlinger::unlockClients()
+{
+ const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
+ const size_t count = drawingLayers.size();
+ sp<LayerBase> const* const layers = drawingLayers.array();
+ for (size_t i=0 ; i<count ; ++i) {
+ const sp<LayerBase>& layer = layers[i];
+ layer->finishPageFlip();
+ }
+}
+
+void SurfaceFlinger::debugFlashRegions()
+{
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t flags = hw.getFlags();
+
+ if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
+ (flags & DisplayHardware::BUFFER_PRESERVED))) {
+ const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ?
+ mDirtyRegion.bounds() : hw.bounds());
+ composeSurfaces(repaint);
+ }
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glDisable(GL_DITHER);
+ glDisable(GL_SCISSOR_TEST);
+
+ static int toggle = 0;
+ toggle = 1 - toggle;
+ if (toggle) {
+ glColor4x(0x10000, 0, 0x10000, 0x10000);
+ } else {
+ glColor4x(0x10000, 0x10000, 0, 0x10000);
+ }
+
+ Region::const_iterator it = mDirtyRegion.begin();
+ Region::const_iterator const end = mDirtyRegion.end();
+ while (it != end) {
+ const Rect& r = *it++;
+ GLfloat vertices[][2] = {
+ { r.left, r.top },
+ { r.left, r.bottom },
+ { r.right, r.bottom },
+ { r.right, r.top }
+ };
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+
+ if (mInvalidRegion.isEmpty()) {
+ mDirtyRegion.dump("mDirtyRegion");
+ mInvalidRegion.dump("mInvalidRegion");
+ }
+ hw.flip(mInvalidRegion);
+
+ if (mDebugRegion > 1)
+ usleep(mDebugRegion * 1000);
+
+ glEnable(GL_SCISSOR_TEST);
+ //mDirtyRegion.dump("mDirtyRegion");
+}
+
+void SurfaceFlinger::drawWormhole() const
+{
+ const Region region(mWormholeRegion.intersect(mDirtyRegion));
+ if (region.isEmpty())
+ return;
+
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const int32_t width = hw.getWidth();
+ const int32_t height = hw.getHeight();
+
+ glDisable(GL_BLEND);
+ glDisable(GL_DITHER);
+
+ if (LIKELY(!mDebugBackground)) {
+ glClearColorx(0,0,0,0);
+ Region::const_iterator it = region.begin();
+ Region::const_iterator const end = region.end();
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = height - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ } else {
+ const GLshort vertices[][2] = { { 0, 0 }, { width, 0 },
+ { width, height }, { 0, height } };
+ const GLshort tcoords[][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } };
+ glVertexPointer(2, GL_SHORT, 0, vertices);
+ glTexCoordPointer(2, GL_SHORT, 0, tcoords);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1);
+ Region::const_iterator it = region.begin();
+ Region::const_iterator const end = region.end();
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = height - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+}
+
+void SurfaceFlinger::debugShowFPS() const
+{
+ static int mFrameCount;
+ static int mLastFrameCount = 0;
+ static nsecs_t mLastFpsTime = 0;
+ static float mFps = 0;
+ mFrameCount++;
+ nsecs_t now = systemTime();
+ nsecs_t diff = now - mLastFpsTime;
+ if (diff > ms2ns(250)) {
+ mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff;
+ mLastFpsTime = now;
+ mLastFrameCount = mFrameCount;
+ }
+ // XXX: mFPS has the value we want
+ }
+
+status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
+{
+ Mutex::Autolock _l(mStateLock);
+ addLayer_l(layer);
+ setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
+{
+ Mutex::Autolock _l(mStateLock);
+ status_t err = purgatorizeLayer_l(layer);
+ if (err == NO_ERROR)
+ setTransactionFlags(eTransactionNeeded);
+ return err;
+}
+
+status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
+{
+ layer->forceVisibilityTransaction();
+ setTransactionFlags(eTraversalNeeded);
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
+{
+ if (layer == 0)
+ return BAD_VALUE;
+ ssize_t i = mCurrentState.layersSortedByZ.add(
+ layer, &LayerBase::compareCurrentStateZ);
+ sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ if (lbc != 0) {
+ mLayerMap.add(lbc->serverIndex(), lbc);
+ }
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
+{
+ ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase);
+ if (index >= 0) {
+ mLayersRemoved = true;
+ sp<LayerBaseClient> layer =
+ LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get());
+ if (layer != 0) {
+ mLayerMap.removeItem(layer->serverIndex());
+ }
+ return NO_ERROR;
+ }
+ return status_t(index);
+}
+
+status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase)
+{
+ // remove the layer from the main list (through a transaction).
+ ssize_t err = removeLayer_l(layerBase);
+
+ layerBase->onRemoved();
+
+ // it's possible that we don't find a layer, because it might
+ // have been destroyed already -- this is not technically an error
+ // from the user because there is a race between BClient::destroySurface(),
+ // ~BClient() and ~ISurface().
+ return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err;
+}
+
+
+void SurfaceFlinger::free_resources_l()
+{
+ // free resources associated with disconnected clients
+ Vector< sp<Client> >& disconnectedClients(mDisconnectedClients);
+ const size_t count = disconnectedClients.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<Client> client = disconnectedClients[i];
+ mTokens.release(client->cid);
+ }
+ disconnectedClients.clear();
+}
+
+uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
+{
+ return android_atomic_and(~flags, &mTransactionFlags) & flags;
+}
+
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay)
+{
+ uint32_t old = android_atomic_or(flags, &mTransactionFlags);
+ if ((old & flags)==0) { // wake the server up
+ if (delay > 0) {
+ signalDelayedEvent(delay);
+ } else {
+ signalEvent();
+ }
+ }
+ return old;
+}
+
+void SurfaceFlinger::openGlobalTransaction()
+{
+ android_atomic_inc(&mTransactionCount);
+}
+
+void SurfaceFlinger::closeGlobalTransaction()
+{
+ if (android_atomic_dec(&mTransactionCount) == 1) {
+ signalEvent();
+
+ // if there is a transaction with a resize, wait for it to
+ // take effect before returning.
+ Mutex::Autolock _l(mStateLock);
+ while (mResizeTransationPending) {
+ status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ // just in case something goes wrong in SF, return to the
+ // called after a few seconds.
+ LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
+ mResizeTransationPending = false;
+ break;
+ }
+ }
+ }
+}
+
+status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags)
+{
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ Mutex::Autolock _l(mStateLock);
+ mCurrentState.freezeDisplay = 1;
+ setTransactionFlags(eTransactionNeeded);
+
+ // flags is intended to communicate some sort of animation behavior
+ // (for instance fading)
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags)
+{
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ Mutex::Autolock _l(mStateLock);
+ mCurrentState.freezeDisplay = 0;
+ setTransactionFlags(eTransactionNeeded);
+
+ // flags is intended to communicate some sort of animation behavior
+ // (for instance fading)
+ return NO_ERROR;
+}
+
+int SurfaceFlinger::setOrientation(DisplayID dpy,
+ int orientation, uint32_t flags)
+{
+ if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+ return BAD_VALUE;
+
+ Mutex::Autolock _l(mStateLock);
+ if (mCurrentState.orientation != orientation) {
+ if (uint32_t(orientation)<=eOrientation270 || orientation==42) {
+ mCurrentState.orientationType = flags;
+ mCurrentState.orientation = orientation;
+ setTransactionFlags(eTransactionNeeded);
+ mTransactionCV.wait(mStateLock);
+ } else {
+ orientation = BAD_VALUE;
+ }
+ }
+ return orientation;
+}
+
+sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
+ const String8& name, ISurfaceFlingerClient::surface_data_t* params,
+ DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags)
+{
+ sp<LayerBaseClient> layer;
+ sp<LayerBaseClient::Surface> surfaceHandle;
+
+ if (int32_t(w|h) < 0) {
+ LOGE("createSurface() failed, w or h is negative (w=%d, h=%d)",
+ int(w), int(h));
+ return surfaceHandle;
+ }
+
+ Mutex::Autolock _l(mStateLock);
+ sp<Client> client = mClientsMap.valueFor(clientId);
+ if (UNLIKELY(client == 0)) {
+ LOGE("createSurface() failed, client not found (id=%d)", clientId);
+ return surfaceHandle;
+ }
+
+ //LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
+ int32_t id = client->generateId(pid);
+ if (uint32_t(id) >= NUM_LAYERS_MAX) {
+ LOGE("createSurface() failed, generateId = %d", id);
+ return surfaceHandle;
+ }
+
+ switch (flags & eFXSurfaceMask) {
+ case eFXSurfaceNormal:
+ if (UNLIKELY(flags & ePushBuffers)) {
+ layer = createPushBuffersSurfaceLocked(client, d, id,
+ w, h, flags);
+ } else {
+ layer = createNormalSurfaceLocked(client, d, id,
+ w, h, flags, format);
+ }
+ break;
+ case eFXSurfaceBlur:
+ layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
+ break;
+ case eFXSurfaceDim:
+ layer = createDimSurfaceLocked(client, d, id, w, h, flags);
+ break;
+ }
+
+ if (layer != 0) {
+ layer->setName(name);
+ setTransactionFlags(eTransactionNeeded);
+ surfaceHandle = layer->getSurface();
+ if (surfaceHandle != 0) {
+ params->token = surfaceHandle->getToken();
+ params->identity = surfaceHandle->getIdentity();
+ params->width = w;
+ params->height = h;
+ params->format = format;
+ }
+ }
+
+ return surfaceHandle;
+}
+
+sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ PixelFormat& format)
+{
+ // initialize the surfaces
+ switch (format) { // TODO: take h/w into account
+ case PIXEL_FORMAT_TRANSPARENT:
+ case PIXEL_FORMAT_TRANSLUCENT:
+ format = PIXEL_FORMAT_RGBA_8888;
+ break;
+ case PIXEL_FORMAT_OPAQUE:
+ format = PIXEL_FORMAT_RGB_565;
+ break;
+ }
+
+ sp<Layer> layer = new Layer(this, display, client, id);
+ status_t err = layer->setBuffers(w, h, format, flags);
+ if (LIKELY(err == NO_ERROR)) {
+ layer->initStates(w, h, flags);
+ addLayer_l(layer);
+ } else {
+ LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
+ layer.clear();
+ }
+ return layer;
+}
+
+sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+{
+ sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
+ layer->initStates(w, h, flags);
+ addLayer_l(layer);
+ return layer;
+}
+
+sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+{
+ sp<LayerDim> layer = new LayerDim(this, display, client, id);
+ layer->initStates(w, h, flags);
+ addLayer_l(layer);
+ return layer;
+}
+
+sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags)
+{
+ sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
+ layer->initStates(w, h, flags);
+ addLayer_l(layer);
+ return layer;
+}
+
+status_t SurfaceFlinger::removeSurface(SurfaceID index)
+{
+ /*
+ * called by the window manager, when a surface should be marked for
+ * destruction.
+ *
+ * The surface is removed from the current and drawing lists, but placed
+ * in the purgatory queue, so it's not destroyed right-away (we need
+ * to wait for all client's references to go away first).
+ */
+
+ status_t err = NAME_NOT_FOUND;
+ Mutex::Autolock _l(mStateLock);
+ sp<LayerBaseClient> layer = getLayerUser_l(index);
+ if (layer != 0) {
+ err = purgatorizeLayer_l(layer);
+ if (err == NO_ERROR) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+ }
+ return err;
+}
+
+status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
+{
+ // called by ~ISurface() when all references are gone
+
+ class MessageDestroySurface : public MessageBase {
+ SurfaceFlinger* flinger;
+ sp<LayerBaseClient> layer;
+ public:
+ MessageDestroySurface(
+ SurfaceFlinger* flinger, const sp<LayerBaseClient>& layer)
+ : flinger(flinger), layer(layer) { }
+ virtual bool handler() {
+ sp<LayerBaseClient> l(layer);
+ layer.clear(); // clear it outside of the lock;
+ Mutex::Autolock _l(flinger->mStateLock);
+ /*
+ * remove the layer from the current list -- chances are that it's
+ * not in the list anyway, because it should have been removed
+ * already upon request of the client (eg: window manager).
+ * However, a buggy client could have not done that.
+ * Since we know we don't have any more clients, we don't need
+ * to use the purgatory.
+ */
+ status_t err = flinger->removeLayer_l(l);
+ LOGE_IF(err<0 && err != NAME_NOT_FOUND,
+ "error removing layer=%p (%s)", l.get(), strerror(-err));
+ return true;
+ }
+ };
+
+ mEventQueue.postMessage( new MessageDestroySurface(this, layer) );
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setClientState(
+ ClientID cid,
+ int32_t count,
+ const layer_state_t* states)
+{
+ Mutex::Autolock _l(mStateLock);
+ uint32_t flags = 0;
+ cid <<= 16;
+ for (int i=0 ; i<count ; i++) {
+ const layer_state_t& s = states[i];
+ sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid));
+ if (layer != 0) {
+ const uint32_t what = s.what;
+ if (what & ePositionChanged) {
+ if (layer->setPosition(s.x, s.y))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eLayerChanged) {
+ if (layer->setLayer(s.z)) {
+ mCurrentState.layersSortedByZ.reorder(
+ layer, &Layer::compareCurrentStateZ);
+ // we need traversal (state changed)
+ // AND transaction (list changed)
+ flags |= eTransactionNeeded|eTraversalNeeded;
+ }
+ }
+ if (what & eSizeChanged) {
+ if (layer->setSize(s.w, s.h)) {
+ flags |= eTraversalNeeded;
+ mResizeTransationPending = true;
+ }
+ }
+ if (what & eAlphaChanged) {
+ if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eMatrixChanged) {
+ if (layer->setMatrix(s.matrix))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eTransparentRegionChanged) {
+ if (layer->setTransparentRegionHint(s.transparentRegion))
+ flags |= eTraversalNeeded;
+ }
+ if (what & eVisibilityChanged) {
+ if (layer->setFlags(s.flags, s.mask))
+ flags |= eTraversalNeeded;
+ }
+ }
+ }
+ if (flags) {
+ setTransactionFlags(flags);
+ }
+ return NO_ERROR;
+}
+
+sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const
+{
+ sp<LayerBaseClient> layer = mLayerMap.valueFor(s);
+ return layer;
+}
+
+void SurfaceFlinger::screenReleased(int dpy)
+{
+ // this may be called by a signal handler, we can't do too much in here
+ android_atomic_or(eConsoleReleased, &mConsoleSignals);
+ signalEvent();
+}
+
+void SurfaceFlinger::screenAcquired(int dpy)
+{
+ // this may be called by a signal handler, we can't do too much in here
+ android_atomic_or(eConsoleAcquired, &mConsoleSignals);
+ signalEvent();
+}
+
+status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 1024;
+ char buffer[SIZE];
+ String8 result;
+ if (!mDump.checkCalling()) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump SurfaceFlinger from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ } else {
+
+ // figure out if we're stuck somewhere
+ const nsecs_t now = systemTime();
+ const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
+ const nsecs_t inTransaction(mDebugInTransaction);
+ nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
+ nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
+
+ // Try to get the main lock, but don't insist if we can't
+ // (this would indicate SF is stuck, but we want to be able to
+ // print something in dumpsys).
+ int retry = 3;
+ while (mStateLock.tryLock()<0 && --retry>=0) {
+ usleep(1000000);
+ }
+ const bool locked(retry >= 0);
+ if (!locked) {
+ snprintf(buffer, SIZE,
+ "SurfaceFlinger appears to be unresponsive, "
+ "dumping anyways (no locks held)\n");
+ result.append(buffer);
+ }
+
+ size_t s = mClientsMap.size();
+ char name[64];
+ for (size_t i=0 ; i<s ; i++) {
+ sp<Client> client = mClientsMap.valueAt(i);
+ sprintf(name, " Client (id=0x%08x)", client->cid);
+ client->dump(name);
+ }
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ /*** LayerBase ***/
+ const sp<LayerBase>& layer = currentLayers[i];
+ const Layer::State& s = layer->drawingState();
+ snprintf(buffer, SIZE,
+ "+ %s %p\n"
+ " "
+ "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
+ "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, "
+ "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
+ layer->getTypeID(), layer.get(),
+ s.z, layer->tx(), layer->ty(), s.w, s.h,
+ layer->needsBlending(), layer->needsDithering(),
+ layer->contentDirty,
+ s.alpha, s.flags,
+ s.transform[0][0], s.transform[0][1],
+ s.transform[1][0], s.transform[1][1]);
+ result.append(buffer);
+ buffer[0] = 0;
+ /*** LayerBaseClient ***/
+ sp<LayerBaseClient> lbc =
+ LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ if (lbc != 0) {
+ sp<Client> client(lbc->client.promote());
+ snprintf(buffer, SIZE,
+ " name=%s\n", lbc->getName().string());
+ result.append(buffer);
+ snprintf(buffer, SIZE,
+ " id=0x%08x, client=0x%08x, identity=%u\n",
+ lbc->clientIndex(), client.get() ? client->cid : 0,
+ lbc->getIdentity());
+
+ result.append(buffer);
+ buffer[0] = 0;
+ }
+ /*** Layer ***/
+ sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get());
+ if (l != 0) {
+ SharedBufferStack::Statistics stats = l->lcblk->getStats();
+ result.append( l->lcblk->dump(" ") );
+ sp<const GraphicBuffer> buf0(l->getBuffer(0));
+ sp<const GraphicBuffer> buf1(l->getBuffer(1));
+ uint32_t w0=0, h0=0, s0=0;
+ uint32_t w1=0, h1=0, s1=0;
+ if (buf0 != 0) {
+ w0 = buf0->getWidth();
+ h0 = buf0->getHeight();
+ s0 = buf0->getStride();
+ }
+ if (buf1 != 0) {
+ w1 = buf1->getWidth();
+ h1 = buf1->getHeight();
+ s1 = buf1->getStride();
+ }
+ snprintf(buffer, SIZE,
+ " "
+ "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
+ " freezeLock=%p, dq-q-time=%u us\n",
+ l->pixelFormat(),
+ w0, h0, s0, w1, h1, s1,
+ l->getFreezeLock().get(), stats.totalTime);
+ result.append(buffer);
+ buffer[0] = 0;
+ }
+ s.transparentRegion.dump(result, "transparentRegion");
+ layer->transparentRegionScreen.dump(result, "transparentRegionScreen");
+ layer->visibleRegionScreen.dump(result, "visibleRegionScreen");
+ }
+ mWormholeRegion.dump(result, "WormholeRegion");
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ snprintf(buffer, SIZE,
+ " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n",
+ mFreezeDisplay?"yes":"no", mFreezeCount,
+ mCurrentState.orientation, hw.canDraw());
+ result.append(buffer);
+ snprintf(buffer, SIZE,
+ " last eglSwapBuffers() time: %f us\n"
+ " last transaction time : %f us\n",
+ mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0);
+ result.append(buffer);
+ if (inSwapBuffersDuration || !locked) {
+ snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
+ inSwapBuffersDuration/1000.0);
+ result.append(buffer);
+ }
+ if (inTransactionDuration || !locked) {
+ snprintf(buffer, SIZE, " transaction time: %f us\n",
+ inTransactionDuration/1000.0);
+ result.append(buffer);
+ }
+ snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size());
+ result.append(buffer);
+ const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
+ alloc.dump(result);
+
+ if (locked) {
+ mStateLock.unlock();
+ }
+ }
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case CREATE_CONNECTION:
+ case OPEN_GLOBAL_TRANSACTION:
+ case CLOSE_GLOBAL_TRANSACTION:
+ case SET_ORIENTATION:
+ case FREEZE_DISPLAY:
+ case UNFREEZE_DISPLAY:
+ case BOOT_FINISHED:
+ {
+ // codes that require permission check
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ }
+ }
+ status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
+ if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ if (UNLIKELY(!mHardwareTest.checkCalling())) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ int n;
+ switch (code) {
+ case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
+ return NO_ERROR;
+ case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
+ return NO_ERROR;
+ case 1002: // SHOW_UPDATES
+ n = data.readInt32();
+ mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
+ return NO_ERROR;
+ case 1003: // SHOW_BACKGROUND
+ n = data.readInt32();
+ mDebugBackground = n ? 1 : 0;
+ return NO_ERROR;
+ case 1004:{ // repaint everything
+ Mutex::Autolock _l(mStateLock);
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe
+ signalEvent();
+ return NO_ERROR;
+ }
+ case 1005:{ // force transaction
+ setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
+ return NO_ERROR;
+ }
+ case 1007: // set mFreezeCount
+ mFreezeCount = data.readInt32();
+ mFreezeDisplayTime = 0;
+ return NO_ERROR;
+ case 1010: // interrogate.
+ reply->writeInt32(0);
+ reply->writeInt32(0);
+ reply->writeInt32(mDebugRegion);
+ reply->writeInt32(mDebugBackground);
+ return NO_ERROR;
+ case 1013: {
+ Mutex::Autolock _l(mStateLock);
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ reply->writeInt32(hw.getPageFlipCount());
+ }
+ return NO_ERROR;
+ }
+ }
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
+ : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)
+{
+ const int pgsize = getpagesize();
+ const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
+
+ mCblkHeap = new MemoryHeapBase(cblksize, 0,
+ "SurfaceFlinger Client control-block");
+
+ ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
+ if (ctrlblk) { // construct the shared structure in-place.
+ new(ctrlblk) SharedClient;
+ }
+}
+
+Client::~Client() {
+ if (ctrlblk) {
+ ctrlblk->~SharedClient(); // destroy our shared-structure.
+ }
+}
+
+int32_t Client::generateId(int pid)
+{
+ const uint32_t i = clz( ~mBitmap );
+ if (i >= NUM_LAYERS_MAX) {
+ return NO_MEMORY;
+ }
+ mPid = pid;
+ mInUse.add(uint8_t(i));
+ mBitmap |= 1<<(31-i);
+ return i;
+}
+
+status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)
+{
+ ssize_t idx = mInUse.indexOf(id);
+ if (idx < 0)
+ return NAME_NOT_FOUND;
+ return mLayers.insertAt(layer, idx);
+}
+
+void Client::free(int32_t id)
+{
+ ssize_t idx = mInUse.remove(uint8_t(id));
+ if (idx >= 0) {
+ mBitmap &= ~(1<<(31-id));
+ mLayers.removeItemsAt(idx);
+ }
+}
+
+bool Client::isValid(int32_t i) const {
+ return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i)));
+}
+
+sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
+ sp<LayerBaseClient> lbc;
+ ssize_t idx = mInUse.indexOf(uint8_t(i));
+ if (idx >= 0) {
+ lbc = mLayers[idx].promote();
+ LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx));
+ }
+ return lbc;
+}
+
+void Client::dump(const char* what)
+{
+}
+
+// ---------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#endif
+
+BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)
+ : mId(cid), mFlinger(flinger), mCblk(cblk)
+{
+}
+
+BClient::~BClient() {
+ // destroy all resources attached to this client
+ mFlinger->destroyConnection(mId);
+}
+
+sp<IMemoryHeap> BClient::getControlBlock() const {
+ return mCblk;
+}
+
+sp<ISurface> BClient::createSurface(
+ ISurfaceFlingerClient::surface_data_t* params, int pid,
+ const String8& name,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags)
+{
+ return mFlinger->createSurface(mId, pid, name, params, display, w, h,
+ format, flags);
+}
+
+status_t BClient::destroySurface(SurfaceID sid)
+{
+ sid |= (mId << 16); // add the client-part to id
+ return mFlinger->removeSurface(sid);
+}
+
+status_t BClient::setState(int32_t count, const layer_state_t* states)
+{
+ return mFlinger->setClientState(mId, count, states);
+}
+
+// ---------------------------------------------------------------------------
+
+GraphicPlane::GraphicPlane()
+ : mHw(0)
+{
+}
+
+GraphicPlane::~GraphicPlane() {
+ delete mHw;
+}
+
+bool GraphicPlane::initialized() const {
+ return mHw ? true : false;
+}
+
+int GraphicPlane::getWidth() const {
+ return mWidth;
+}
+
+int GraphicPlane::getHeight() const {
+ return mHeight;
+}
+
+void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
+{
+ mHw = hw;
+
+ // initialize the display orientation transform.
+ // it's a constant that should come from the display driver.
+ int displayOrientation = ISurfaceComposer::eOrientationDefault;
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
+ //displayOrientation
+ switch (atoi(property)) {
+ case 90:
+ displayOrientation = ISurfaceComposer::eOrientation90;
+ break;
+ case 270:
+ displayOrientation = ISurfaceComposer::eOrientation270;
+ break;
+ }
+ }
+
+ const float w = hw->getWidth();
+ const float h = hw->getHeight();
+ GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
+ &mDisplayTransform);
+ if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
+ mDisplayWidth = h;
+ mDisplayHeight = w;
+ } else {
+ mDisplayWidth = w;
+ mDisplayHeight = h;
+ }
+
+ setOrientation(ISurfaceComposer::eOrientationDefault);
+}
+
+status_t GraphicPlane::orientationToTransfrom(
+ int orientation, int w, int h, Transform* tr)
+{
+ uint32_t flags = 0;
+ switch (orientation) {
+ case ISurfaceComposer::eOrientationDefault:
+ flags = Transform::ROT_0;
+ break;
+ case ISurfaceComposer::eOrientation90:
+ flags = Transform::ROT_90;
+ break;
+ case ISurfaceComposer::eOrientation180:
+ flags = Transform::ROT_180;
+ break;
+ case ISurfaceComposer::eOrientation270:
+ flags = Transform::ROT_270;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+ tr->set(flags, w, h);
+ return NO_ERROR;
+}
+
+status_t GraphicPlane::setOrientation(int orientation)
+{
+ // If the rotation can be handled in hardware, this is where
+ // the magic should happen.
+
+ const DisplayHardware& hw(displayHardware());
+ const float w = mDisplayWidth;
+ const float h = mDisplayHeight;
+ mWidth = int(w);
+ mHeight = int(h);
+
+ Transform orientationTransform;
+ GraphicPlane::orientationToTransfrom(orientation, w, h,
+ &orientationTransform);
+ if (orientation & ISurfaceComposer::eOrientationSwapMask) {
+ mWidth = int(h);
+ mHeight = int(w);
+ }
+
+ mOrientation = orientation;
+ mGlobalTransform = mDisplayTransform * orientationTransform;
+ return NO_ERROR;
+}
+
+const DisplayHardware& GraphicPlane::displayHardware() const {
+ return *mHw;
+}
+
+const Transform& GraphicPlane::transform() const {
+ return mGlobalTransform;
+}
+
+EGLDisplay GraphicPlane::getEGLDisplay() const {
+ return mHw->getEGLDisplay();
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
new file mode 100644
index 0000000..d75dc15
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_H
+#define ANDROID_SURFACE_FLINGER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include <binder/IMemory.h>
+#include <binder/Permission.h>
+
+#include <ui/PixelFormat.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+#include <surfaceflinger/ISurfaceFlingerClient.h>
+
+#include "Barrier.h"
+#include "Layer.h"
+#include "Tokenizer.h"
+
+#include "MessageQueue.h"
+
+struct copybit_device_t;
+struct overlay_device_t;
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class Client;
+class BClient;
+class DisplayHardware;
+class FreezeLock;
+class Layer;
+class LayerBuffer;
+
+typedef int32_t ClientID;
+
+#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
+#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
+
+// ---------------------------------------------------------------------------
+
+class Client : public RefBase
+{
+public:
+ Client(ClientID cid, const sp<SurfaceFlinger>& flinger);
+ ~Client();
+
+ int32_t generateId(int pid);
+ void free(int32_t id);
+ status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id);
+
+ inline bool isValid(int32_t i) const;
+ sp<LayerBaseClient> getLayerUser(int32_t i) const;
+ void dump(const char* what);
+
+ const Vector< wp<LayerBaseClient> >& getLayers() const {
+ return mLayers;
+ }
+
+ const sp<IMemoryHeap>& getControlBlockMemory() const {
+ return mCblkHeap;
+ }
+
+ // pointer to this client's control block
+ SharedClient* ctrlblk;
+ ClientID cid;
+
+
+private:
+ int getClientPid() const { return mPid; }
+
+ int mPid;
+ uint32_t mBitmap;
+ SortedVector<uint8_t> mInUse;
+ Vector< wp<LayerBaseClient> > mLayers;
+ sp<IMemoryHeap> mCblkHeap;
+ sp<SurfaceFlinger> mFlinger;
+};
+
+// ---------------------------------------------------------------------------
+
+class GraphicPlane
+{
+public:
+ static status_t orientationToTransfrom(int orientation, int w, int h,
+ Transform* tr);
+
+ GraphicPlane();
+ ~GraphicPlane();
+
+ bool initialized() const;
+
+ void setDisplayHardware(DisplayHardware *);
+ status_t setOrientation(int orientation);
+ int getOrientation() const { return mOrientation; }
+ int getWidth() const;
+ int getHeight() const;
+
+ const DisplayHardware& displayHardware() const;
+ const Transform& transform() const;
+ EGLDisplay getEGLDisplay() const;
+
+private:
+ GraphicPlane(const GraphicPlane&);
+ GraphicPlane operator = (const GraphicPlane&);
+
+ DisplayHardware* mHw;
+ Transform mGlobalTransform;
+ Transform mDisplayTransform;
+ int mOrientation;
+ float mDisplayWidth;
+ float mDisplayHeight;
+ int mWidth;
+ int mHeight;
+};
+
+// ---------------------------------------------------------------------------
+
+enum {
+ eTransactionNeeded = 0x01,
+ eTraversalNeeded = 0x02
+};
+
+class SurfaceFlinger : public BnSurfaceComposer, protected Thread
+{
+public:
+ static void instantiate();
+ static void shutdown();
+
+ SurfaceFlinger();
+ virtual ~SurfaceFlinger();
+ void init();
+
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ // ISurfaceComposer interface
+ virtual sp<ISurfaceFlingerClient> createConnection();
+ virtual sp<IMemoryHeap> getCblk() const;
+ virtual void bootFinished();
+ virtual void openGlobalTransaction();
+ virtual void closeGlobalTransaction();
+ virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags);
+ virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
+ virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
+ virtual void signal() const;
+
+ void screenReleased(DisplayID dpy);
+ void screenAcquired(DisplayID dpy);
+
+ overlay_control_device_t* getOverlayEngine() const;
+
+
+ status_t removeLayer(const sp<LayerBase>& layer);
+ status_t addLayer(const sp<LayerBase>& layer);
+ status_t invalidateLayerVisibility(const sp<LayerBase>& layer);
+
+private:
+ friend class BClient;
+ friend class LayerBase;
+ friend class LayerBuffer;
+ friend class LayerBaseClient;
+ friend class LayerBaseClient::Surface;
+ friend class Layer;
+ friend class LayerBlur;
+ friend class LayerDim;
+
+ sp<ISurface> createSurface(ClientID client, int pid, const String8& name,
+ ISurfaceFlingerClient::surface_data_t* params,
+ DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t flags);
+
+ sp<LayerBaseClient> createNormalSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ PixelFormat& format);
+
+ sp<LayerBaseClient> createBlurSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+
+ sp<LayerBaseClient> createDimSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+
+ sp<LayerBaseClient> createPushBuffersSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags);
+
+ status_t removeSurface(SurfaceID surface_id);
+ status_t destroySurface(const sp<LayerBaseClient>& layer);
+ status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
+
+
+ class LayerVector {
+ public:
+ inline LayerVector() { }
+ LayerVector(const LayerVector&);
+ inline size_t size() const { return layers.size(); }
+ inline sp<LayerBase> const* array() const { return layers.array(); }
+ ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
+ ssize_t remove(const sp<LayerBase>&);
+ ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
+ ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const;
+ inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
+ private:
+ KeyedVector< sp<LayerBase> , size_t> lookup;
+ Vector< sp<LayerBase> > layers;
+ };
+
+ struct State {
+ State() {
+ orientation = ISurfaceComposer::eOrientationDefault;
+ freezeDisplay = 0;
+ }
+ LayerVector layersSortedByZ;
+ uint8_t orientation;
+ uint8_t orientationType;
+ uint8_t freezeDisplay;
+ };
+
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+ virtual void onFirstRef();
+
+public: // hack to work around gcc 4.0.3 bug
+ const GraphicPlane& graphicPlane(int dpy) const;
+ GraphicPlane& graphicPlane(int dpy);
+private:
+
+ void waitForEvent();
+public: // hack to work around gcc 4.0.3 bug
+ void signalEvent();
+private:
+ void signalDelayedEvent(nsecs_t delay);
+
+ void handleConsoleEvents();
+ void handleTransaction(uint32_t transactionFlags);
+ void handleTransactionLocked(
+ uint32_t transactionFlags,
+ Vector< sp<LayerBase> >& ditchedLayers);
+
+ void computeVisibleRegions(
+ LayerVector& currentLayers,
+ Region& dirtyRegion,
+ Region& wormholeRegion);
+
+ void handlePageFlip();
+ bool lockPageFlip(const LayerVector& currentLayers);
+ void unlockPageFlip(const LayerVector& currentLayers);
+ void handleRepaint();
+ void postFramebuffer();
+ void composeSurfaces(const Region& dirty);
+ void unlockClients();
+
+
+ void destroyConnection(ClientID cid);
+ sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
+ status_t addLayer_l(const sp<LayerBase>& layer);
+ status_t removeLayer_l(const sp<LayerBase>& layer);
+ status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
+ void free_resources_l();
+
+ uint32_t getTransactionFlags(uint32_t flags);
+ uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0);
+ void commitTransaction();
+
+
+ friend class FreezeLock;
+ sp<FreezeLock> getFreezeLock() const;
+ inline void incFreezeCount() {
+ if (mFreezeCount == 0)
+ mFreezeDisplayTime = 0;
+ mFreezeCount++;
+ }
+ inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; }
+ inline bool hasFreezeRequest() const { return mFreezeDisplay; }
+ inline bool isFrozen() const {
+ return (mFreezeDisplay || mFreezeCount>0) && mBootFinished;
+ }
+
+
+ void debugFlashRegions();
+ void debugShowFPS() const;
+ void drawWormhole() const;
+
+
+ mutable MessageQueue mEventQueue;
+
+
+
+ // access must be protected by mStateLock
+ mutable Mutex mStateLock;
+ State mCurrentState;
+ State mDrawingState;
+ volatile int32_t mTransactionFlags;
+ volatile int32_t mTransactionCount;
+ Condition mTransactionCV;
+ bool mResizeTransationPending;
+
+ // protected by mStateLock (but we could use another lock)
+ Tokenizer mTokens;
+ DefaultKeyedVector<ClientID, sp<Client> > mClientsMap;
+ DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap;
+ GraphicPlane mGraphicPlanes[1];
+ bool mLayersRemoved;
+ Vector< sp<Client> > mDisconnectedClients;
+
+ // constant members (no synchronization needed for access)
+ sp<IMemoryHeap> mServerHeap;
+ surface_flinger_cblk_t* mServerCblk;
+ GLuint mWormholeTexName;
+ nsecs_t mBootTime;
+ Permission mHardwareTest;
+ Permission mAccessSurfaceFlinger;
+ Permission mDump;
+
+ // Can only accessed from the main thread, these members
+ // don't need synchronization
+ Region mDirtyRegion;
+ Region mDirtyRegionRemovedLayer;
+ Region mInvalidRegion;
+ Region mWormholeRegion;
+ bool mVisibleRegionsDirty;
+ bool mDeferReleaseConsole;
+ bool mFreezeDisplay;
+ int32_t mFreezeCount;
+ nsecs_t mFreezeDisplayTime;
+
+ // don't use a lock for these, we don't care
+ int mDebugRegion;
+ int mDebugBackground;
+ volatile nsecs_t mDebugInSwapBuffers;
+ nsecs_t mLastSwapBufferTime;
+ volatile nsecs_t mDebugInTransaction;
+ nsecs_t mLastTransactionTime;
+ bool mBootFinished;
+
+ // these are thread safe
+ mutable Barrier mReadyToRunBarrier;
+
+ // atomic variables
+ enum {
+ eConsoleReleased = 1,
+ eConsoleAcquired = 2
+ };
+ volatile int32_t mConsoleSignals;
+
+ // only written in the main thread, only read in other threads
+ volatile int32_t mSecureFrameBuffer;
+};
+
+// ---------------------------------------------------------------------------
+
+class FreezeLock : public LightRefBase<FreezeLock> {
+ SurfaceFlinger* mFlinger;
+public:
+ FreezeLock(SurfaceFlinger* flinger)
+ : mFlinger(flinger) {
+ mFlinger->incFreezeCount();
+ }
+ ~FreezeLock() {
+ mFlinger->decFreezeCount();
+ }
+};
+
+// ---------------------------------------------------------------------------
+
+class BClient : public BnSurfaceFlingerClient
+{
+public:
+ BClient(SurfaceFlinger *flinger, ClientID cid,
+ const sp<IMemoryHeap>& cblk);
+ ~BClient();
+
+ // ISurfaceFlingerClient interface
+ virtual sp<IMemoryHeap> getControlBlock() const;
+
+ virtual sp<ISurface> createSurface(
+ surface_data_t* params, int pid, const String8& name,
+ DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
+ uint32_t flags);
+
+ virtual status_t destroySurface(SurfaceID surfaceId);
+ virtual status_t setState(int32_t count, const layer_state_t* states);
+
+private:
+ ClientID mId;
+ SurfaceFlinger* mFlinger;
+ sp<IMemoryHeap> mCblk;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SURFACE_FLINGER_H
diff --git a/services/surfaceflinger/Tokenizer.cpp b/services/surfaceflinger/Tokenizer.cpp
new file mode 100644
index 0000000..be3a239
--- /dev/null
+++ b/services/surfaceflinger/Tokenizer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "Tokenizer.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t)
+
+Tokenizer::Tokenizer()
+{
+}
+
+Tokenizer::Tokenizer(const Tokenizer& other)
+ : mRanges(other.mRanges)
+{
+}
+
+Tokenizer::~Tokenizer()
+{
+}
+
+uint32_t Tokenizer::acquire()
+{
+ if (!mRanges.size() || mRanges[0].first) {
+ _insertTokenAt(0,0);
+ return 0;
+ }
+
+ // just extend the first run
+ const run_t& run = mRanges[0];
+ uint32_t token = run.first + run.length;
+ _insertTokenAt(token, 1);
+ return token;
+}
+
+bool Tokenizer::isAcquired(uint32_t token) const
+{
+ return (_indexOrderOf(token) >= 0);
+}
+
+status_t Tokenizer::reserve(uint32_t token)
+{
+ size_t o;
+ const ssize_t i = _indexOrderOf(token, &o);
+ if (i >= 0) {
+ return BAD_VALUE; // this token is already taken
+ }
+ ssize_t err = _insertTokenAt(token, o);
+ return (err<0) ? err : status_t(NO_ERROR);
+}
+
+status_t Tokenizer::release(uint32_t token)
+{
+ const ssize_t i = _indexOrderOf(token);
+ if (i >= 0) {
+ const run_t& run = mRanges[i];
+ if ((token >= run.first) && (token < run.first+run.length)) {
+ // token in this range, we need to split
+ run_t& run = mRanges.editItemAt(i);
+ if ((token == run.first) || (token == run.first+run.length-1)) {
+ if (token == run.first) {
+ run.first += 1;
+ }
+ run.length -= 1;
+ if (run.length == 0) {
+ // XXX: should we systematically remove a run that's empty?
+ mRanges.removeItemsAt(i);
+ }
+ } else {
+ // split the run
+ run_t new_run;
+ new_run.first = token+1;
+ new_run.length = run.first+run.length - new_run.first;
+ run.length = token - run.first;
+ mRanges.insertAt(new_run, i+1);
+ }
+ return NO_ERROR;
+ }
+ }
+ return NAME_NOT_FOUND;
+}
+
+ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const
+{
+ // binary search
+ ssize_t err = NAME_NOT_FOUND;
+ ssize_t l = 0;
+ ssize_t h = mRanges.size()-1;
+ ssize_t mid;
+ const run_t* a = mRanges.array();
+ while (l <= h) {
+ mid = l + (h - l)/2;
+ const run_t* const curr = a + mid;
+ int c = 0;
+ if (token < curr->first) c = 1;
+ else if (token >= curr->first+curr->length) c = -1;
+ if (c == 0) {
+ err = l = mid;
+ break;
+ } else if (c < 0) {
+ l = mid + 1;
+ } else {
+ h = mid - 1;
+ }
+ }
+ if (order) *order = l;
+ return err;
+}
+
+ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index)
+{
+ const size_t c = mRanges.size();
+
+ if (index >= 1) {
+ // do we need to merge with the previous run?
+ run_t& p = mRanges.editItemAt(index-1);
+ if (p.first+p.length == token) {
+ p.length += 1;
+ if (index < c) {
+ const run_t& n = mRanges[index];
+ if (token+1 == n.first) {
+ p.length += n.length;
+ mRanges.removeItemsAt(index);
+ }
+ }
+ return index;
+ }
+ }
+
+ if (index < c) {
+ // do we need to merge with the next run?
+ run_t& n = mRanges.editItemAt(index);
+ if (token+1 == n.first) {
+ n.first -= 1;
+ n.length += 1;
+ return index;
+ }
+ }
+
+ return mRanges.insertAt(run_t(token,1), index);
+}
+
+void Tokenizer::dump() const
+{
+ const run_t* ranges = mRanges.array();
+ const size_t c = mRanges.size();
+ printf("Tokenizer (%p, size = %d)\n", this, int(c));
+ for (size_t i=0 ; i<c ; i++) {
+ printf("%u: (%u, %u)\n", i,
+ uint32_t(ranges[i].first), uint32_t(ranges[i].length));
+ }
+}
+
+}; // namespace android
+
diff --git a/services/surfaceflinger/Tokenizer.h b/services/surfaceflinger/Tokenizer.h
new file mode 100644
index 0000000..6b3057d
--- /dev/null
+++ b/services/surfaceflinger/Tokenizer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TOKENIZER_H
+#define ANDROID_TOKENIZER_H
+
+#include <utils/Vector.h>
+#include <utils/Errors.h>
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+class Tokenizer
+{
+public:
+ Tokenizer();
+ Tokenizer(const Tokenizer& other);
+ ~Tokenizer();
+
+ uint32_t acquire();
+ status_t reserve(uint32_t token);
+ status_t release(uint32_t token);
+ bool isAcquired(uint32_t token) const;
+
+ void dump() const;
+
+ struct run_t {
+ run_t() {};
+ run_t(uint32_t f, uint32_t l) : first(f), length(l) {}
+ uint32_t first;
+ uint32_t length;
+ };
+private:
+ ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const;
+ ssize_t _insertTokenAt(uint32_t token, size_t index);
+ Vector<run_t> mRanges;
+};
+
+}; // namespace android
+
+// ----------------------------------------------------------------------------
+
+#endif // ANDROID_TOKENIZER_H
diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp
new file mode 100644
index 0000000..175f989
--- /dev/null
+++ b/services/surfaceflinger/Transform.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+
+#include <cutils/compiler.h>
+#include <utils/String8.h>
+#include <ui/Region.h>
+
+#include "Transform.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+template <typename T> inline T min(T a, T b) {
+ return a<b ? a : b;
+}
+template <typename T> inline T min(T a, T b, T c) {
+ return min(a, min(b, c));
+}
+template <typename T> inline T min(T a, T b, T c, T d) {
+ return min(a, b, min(c, d));
+}
+
+template <typename T> inline T max(T a, T b) {
+ return a>b ? a : b;
+}
+template <typename T> inline T max(T a, T b, T c) {
+ return max(a, max(b, c));
+}
+template <typename T> inline T max(T a, T b, T c, T d) {
+ return max(a, b, max(c, d));
+}
+
+// ---------------------------------------------------------------------------
+
+Transform::Transform() {
+ reset();
+}
+
+Transform::Transform(const Transform& other)
+ : mMatrix(other.mMatrix), mType(other.mType) {
+}
+
+Transform::Transform(uint32_t orientation) {
+ set(orientation, 0, 0);
+}
+
+Transform::~Transform() {
+}
+
+static const float EPSILON = 0.0f;
+
+bool Transform::isZero(float f) {
+ return fabs(f) <= EPSILON;
+}
+
+bool Transform::absIsOne(float f) {
+ return isZero(fabs(f) - 1.0f);
+}
+
+Transform Transform::operator * (const Transform& rhs) const
+{
+ if (CC_LIKELY(mType == IDENTITY))
+ return rhs;
+
+ Transform r(*this);
+ if (rhs.mType == IDENTITY)
+ return r;
+
+ // TODO: we could use mType to optimize the matrix multiply
+ const mat33& A(mMatrix);
+ const mat33& B(rhs.mMatrix);
+ mat33& D(r.mMatrix);
+ for (int i=0 ; i<3 ; i++) {
+ const float v0 = A[0][i];
+ const float v1 = A[1][i];
+ const float v2 = A[2][i];
+ D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2];
+ D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2];
+ D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2];
+ }
+ r.mType |= rhs.mType;
+
+ // TODO: we could recompute this value from r and rhs
+ r.mType &= 0xFF;
+ r.mType |= UNKNOWN_TYPE;
+ return r;
+}
+
+float const* Transform::operator [] (int i) const {
+ return mMatrix[i].v;
+}
+
+bool Transform::transformed() const {
+ return type() > TRANSLATE;
+}
+
+int Transform::tx() const {
+ return floorf(mMatrix[2][0] + 0.5f);
+}
+
+int Transform::ty() const {
+ return floorf(mMatrix[2][1] + 0.5f);
+}
+
+void Transform::reset() {
+ mType = IDENTITY;
+ for(int i=0 ; i<3 ; i++) {
+ vec3& v(mMatrix[i]);
+ for (int j=0 ; j<3 ; j++)
+ v[j] = ((i==j) ? 1.0f : 0.0f);
+ }
+}
+
+void Transform::set(float tx, float ty)
+{
+ mMatrix[2][0] = tx;
+ mMatrix[2][1] = ty;
+ mMatrix[2][2] = 1.0f;
+
+ if (isZero(tx) && isZero(ty)) {
+ mType &= ~TRANSLATE;
+ } else {
+ mType |= TRANSLATE;
+ }
+}
+
+void Transform::set(float a, float b, float c, float d)
+{
+ mat33& M(mMatrix);
+ M[0][0] = a; M[1][0] = b;
+ M[0][1] = c; M[1][1] = d;
+ M[0][2] = 0; M[1][2] = 0;
+ mType = UNKNOWN_TYPE;
+}
+
+status_t Transform::set(uint32_t flags, float w, float h)
+{
+ if (flags & ROT_INVALID) {
+ // that's not allowed!
+ reset();
+ return BAD_VALUE;
+ }
+
+ mType = flags << 8;
+ float sx = (flags & FLIP_H) ? -1 : 1;
+ float sy = (flags & FLIP_V) ? -1 : 1;
+ float a=0, b=0, c=0, d=0, x=0, y=0;
+ int xmask = 0;
+
+ // computation of x,y
+ // x y
+ // 0 0 0
+ // w 0 ROT90
+ // w h FLIPH|FLIPV
+ // 0 h FLIPH|FLIPV|ROT90
+
+ if (flags & ROT_90) {
+ mType |= ROTATE;
+ b = -sy;
+ c = sx;
+ xmask = 1;
+ } else {
+ a = sx;
+ d = sy;
+ }
+
+ if (flags & FLIP_H) {
+ mType ^= SCALE;
+ xmask ^= 1;
+ }
+
+ if (flags & FLIP_V) {
+ mType ^= SCALE;
+ y = h;
+ }
+
+ if ((flags & ROT_180) == ROT_180) {
+ mType |= ROTATE;
+ }
+
+ if (xmask) {
+ x = w;
+ }
+
+ if (!isZero(x) || !isZero(y)) {
+ mType |= TRANSLATE;
+ }
+
+ mat33& M(mMatrix);
+ M[0][0] = a; M[1][0] = b; M[2][0] = x;
+ M[0][1] = c; M[1][1] = d; M[2][1] = y;
+ M[0][2] = 0; M[1][2] = 0; M[2][2] = 1;
+
+ return NO_ERROR;
+}
+
+Transform::vec2 Transform::transform(const vec2& v) const {
+ vec2 r;
+ const mat33& M(mMatrix);
+ r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0];
+ r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1];
+ return r;
+}
+
+Transform::vec3 Transform::transform(const vec3& v) const {
+ vec3 r;
+ const mat33& M(mMatrix);
+ r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2];
+ r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2];
+ r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2];
+ return r;
+}
+
+void Transform::transform(fixed1616* point, int x, int y) const
+{
+ const float toFixed = 65536.0f;
+ const mat33& M(mMatrix);
+ vec2 v(x, y);
+ v = transform(v);
+ point[0] = v[0] * toFixed;
+ point[1] = v[1] * toFixed;
+}
+
+Rect Transform::makeBounds(int w, int h) const
+{
+ return transform( Rect(w, h) );
+}
+
+Rect Transform::transform(const Rect& bounds) const
+{
+ Rect r;
+ vec2 lt( bounds.left, bounds.top );
+ vec2 rt( bounds.right, bounds.top );
+ vec2 lb( bounds.left, bounds.bottom );
+ vec2 rb( bounds.right, bounds.bottom );
+
+ lt = transform(lt);
+ rt = transform(rt);
+ lb = transform(lb);
+ rb = transform(rb);
+
+ r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+ r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+ r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f);
+ r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f);
+
+ return r;
+}
+
+Region Transform::transform(const Region& reg) const
+{
+ Region out;
+ if (CC_UNLIKELY(transformed())) {
+ if (CC_LIKELY(preserveRects())) {
+ Region::const_iterator it = reg.begin();
+ Region::const_iterator const end = reg.end();
+ while (it != end) {
+ out.orSelf(transform(*it++));
+ }
+ } else {
+ out.set(transform(reg.bounds()));
+ }
+ } else {
+ out = reg.translate(tx(), ty());
+ }
+ return out;
+}
+
+uint32_t Transform::type() const
+{
+ if (mType & UNKNOWN_TYPE) {
+ // recompute what this transform is
+
+ const mat33& M(mMatrix);
+ const float a = M[0][0];
+ const float b = M[1][0];
+ const float c = M[0][1];
+ const float d = M[1][1];
+ const float x = M[2][0];
+ const float y = M[2][1];
+
+ bool scale = false;
+ uint32_t flags = ROT_0;
+ if (isZero(b) && isZero(c)) {
+ if (a<0) flags |= FLIP_H;
+ if (d<0) flags |= FLIP_V;
+ if (!absIsOne(a) || !absIsOne(d)) {
+ scale = true;
+ }
+ } else if (isZero(a) && isZero(d)) {
+ flags |= ROT_90;
+ if (b>0) flags |= FLIP_H;
+ if (c<0) flags |= FLIP_V;
+ if (!absIsOne(b) || !absIsOne(c)) {
+ scale = true;
+ }
+ } else {
+ flags = ROT_INVALID;
+ }
+
+ mType = flags << 8;
+ if (flags & ROT_INVALID) {
+ mType |= UNKNOWN;
+ } else {
+ if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180))
+ mType |= ROTATE;
+ if (flags & FLIP_H)
+ mType ^= SCALE;
+ if (flags & FLIP_V)
+ mType ^= SCALE;
+ if (scale)
+ mType |= SCALE;
+ }
+
+ if (!isZero(x) || !isZero(y))
+ mType |= TRANSLATE;
+ }
+ return mType;
+}
+
+uint32_t Transform::getType() const {
+ return type() & 0xFF;
+}
+
+uint32_t Transform::getOrientation() const
+{
+ return (type() >> 8) & 0xFF;
+}
+
+bool Transform::preserveRects() const
+{
+ return (type() & ROT_INVALID) ? false : true;
+}
+
+void Transform::dump(const char* name) const
+{
+ type(); // updates the type
+
+ String8 flags, type;
+ const mat33& m(mMatrix);
+ uint32_t orient = mType >> 8;
+
+ if (orient&ROT_INVALID) {
+ flags.append("ROT_INVALID ");
+ } else {
+ if (orient&ROT_90) {
+ flags.append("ROT_90 ");
+ } else {
+ flags.append("ROT_0 ");
+ }
+ if (orient&FLIP_V)
+ flags.append("FLIP_V ");
+ if (orient&FLIP_H)
+ flags.append("FLIP_H ");
+ }
+
+ if (!(mType&(SCALE|ROTATE|TRANSLATE)))
+ type.append("IDENTITY ");
+ if (mType&SCALE)
+ type.append("SCALE ");
+ if (mType&ROTATE)
+ type.append("ROTATE ");
+ if (mType&TRANSLATE)
+ type.append("TRANSLATE ");
+
+ LOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string());
+ LOGD("%.4f %.4f %.4f", m[0][0], m[1][0], m[2][0]);
+ LOGD("%.4f %.4f %.4f", m[0][1], m[1][1], m[2][1]);
+ LOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]);
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
new file mode 100644
index 0000000..2e5b893
--- /dev/null
+++ b/services/surfaceflinger/Transform.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TRANSFORM_H
+#define ANDROID_TRANSFORM_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <ui/Point.h>
+#include <ui/Rect.h>
+
+namespace android {
+
+class Region;
+
+// ---------------------------------------------------------------------------
+
+class Transform
+{
+public:
+ Transform();
+ Transform(const Transform& other);
+ explicit Transform(uint32_t orientation);
+ ~Transform();
+
+ typedef int32_t fixed1616;
+
+ // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h
+ enum orientation_flags {
+ ROT_0 = 0x00000000,
+ FLIP_H = 0x00000001,
+ FLIP_V = 0x00000002,
+ ROT_90 = 0x00000004,
+ ROT_180 = FLIP_H|FLIP_V,
+ ROT_270 = ROT_180|ROT_90,
+ ROT_INVALID = 0x80
+ };
+
+ enum type_mask {
+ IDENTITY = 0,
+ TRANSLATE = 0x1,
+ ROTATE = 0x2,
+ SCALE = 0x4,
+ UNKNOWN = 0x8
+ };
+
+ // query the transform
+ bool transformed() const;
+ bool preserveRects() const;
+ uint32_t getType() const;
+ uint32_t getOrientation() const;
+
+ float const* operator [] (int i) const; // returns column i
+ int tx() const;
+ int ty() const;
+
+ // modify the transform
+ void reset();
+ void set(float tx, float ty);
+ void set(float a, float b, float c, float d);
+ status_t set(uint32_t flags, float w, float h);
+
+ // transform data
+ Rect makeBounds(int w, int h) const;
+ void transform(fixed1616* point, int x, int y) const;
+ Region transform(const Region& reg) const;
+ Transform operator * (const Transform& rhs) const;
+
+ // for debugging
+ void dump(const char* name) const;
+
+private:
+ struct vec3 {
+ float v[3];
+ inline vec3() { }
+ inline vec3(float a, float b, float c) {
+ v[0] = a; v[1] = b; v[2] = c;
+ }
+ inline float operator [] (int i) const { return v[i]; }
+ inline float& operator [] (int i) { return v[i]; }
+ };
+ struct vec2 {
+ float v[2];
+ inline vec2() { }
+ inline vec2(float a, float b) {
+ v[0] = a; v[1] = b;
+ }
+ inline float operator [] (int i) const { return v[i]; }
+ inline float& operator [] (int i) { return v[i]; }
+ };
+ struct mat33 {
+ vec3 v[3];
+ inline const vec3& operator [] (int i) const { return v[i]; }
+ inline vec3& operator [] (int i) { return v[i]; }
+ };
+
+ enum { UNKNOWN_TYPE = 0x80000000 };
+
+ // assumes the last row is < 0 , 0 , 1 >
+ vec2 transform(const vec2& v) const;
+ vec3 transform(const vec3& v) const;
+ Rect transform(const Rect& bounds) const;
+ uint32_t type() const;
+ static bool absIsOne(float f);
+ static bool isZero(float f);
+
+ mat33 mMatrix;
+ mutable uint32_t mType;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_TRANSFORM_H */
diff --git a/services/surfaceflinger/clz.cpp b/services/surfaceflinger/clz.cpp
new file mode 100644
index 0000000..2456b86
--- /dev/null
+++ b/services/surfaceflinger/clz.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "clz.h"
+
+namespace android {
+
+int clz_impl(int32_t x)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ return __builtin_clz(x);
+#else
+ if (!x) return 32;
+ int e = 31;
+ if (x&0xFFFF0000) { e -=16; x >>=16; }
+ if (x&0x0000FF00) { e -= 8; x >>= 8; }
+ if (x&0x000000F0) { e -= 4; x >>= 4; }
+ if (x&0x0000000C) { e -= 2; x >>= 2; }
+ if (x&0x00000002) { e -= 1; }
+ return e;
+#endif
+}
+
+}; // namespace android
diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h
new file mode 100644
index 0000000..0ddf986
--- /dev/null
+++ b/services/surfaceflinger/clz.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SURFACE_FLINGER_CLZ_H
+
+#include <stdint.h>
+
+namespace android {
+
+int clz_impl(int32_t x);
+
+int inline clz(int32_t x)
+{
+#if defined(__arm__) && !defined(__thumb__)
+ return __builtin_clz(x);
+#else
+ return clz_impl(x);
+#endif
+}
+
+
+}; // namespace android
+
+#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/services/surfaceflinger/tests/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/services/surfaceflinger/tests/overlays/Android.mk b/services/surfaceflinger/tests/overlays/Android.mk
new file mode 100644
index 0000000..592b601
--- /dev/null
+++ b/services/surfaceflinger/tests/overlays/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ overlays.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-overlays
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/overlays/overlays.cpp b/services/surfaceflinger/tests/overlays/overlays.cpp
new file mode 100644
index 0000000..c248a61
--- /dev/null
+++ b/services/surfaceflinger/tests/overlays/overlays.cpp
@@ -0,0 +1,59 @@
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+#include <ui/Overlay.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+using namespace android;
+
+namespace android {
+class Test {
+public:
+ static const sp<ISurface>& getISurface(const sp<Surface>& s) {
+ return s->getISurface();
+ }
+};
+};
+
+int main(int argc, char** argv)
+{
+ // set up the thread-pool
+ sp<ProcessState> proc(ProcessState::self());
+ ProcessState::self()->startThreadPool();
+
+ // create a client to surfaceflinger
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+
+ // create pushbuffer surface
+ sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240,
+ PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);
+
+ // get to the isurface
+ sp<ISurface> isurface = Test::getISurface(surface);
+ printf("isurface = %p\n", isurface.get());
+
+ // now request an overlay
+ sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565);
+ sp<Overlay> overlay = new Overlay(ref);
+
+
+ /*
+ * here we can use the overlay API
+ */
+
+ overlay_buffer_t buffer;
+ overlay->dequeueBuffer(&buffer);
+ printf("buffer = %p\n", buffer);
+
+ void* address = overlay->getBufferAddress(buffer);
+ printf("address = %p\n", address);
+
+ overlay->queueBuffer(buffer);
+
+ return 0;
+}
diff --git a/services/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk
new file mode 100644
index 0000000..24c2d01
--- /dev/null
+++ b/services/surfaceflinger/tests/resize/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ resize.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui \
+ libsurfaceflinger_client
+
+LOCAL_MODULE:= test-resize
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
new file mode 100644
index 0000000..127cca3
--- /dev/null
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -0,0 +1,62 @@
+#include <cutils/memory.h>
+
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include <surfaceflinger/Surface.h>
+#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/SurfaceComposerClient.h>
+
+#include <ui/Overlay.h>
+
+using namespace android;
+
+namespace android {
+class Test {
+public:
+ static const sp<ISurface>& getISurface(const sp<Surface>& s) {
+ return s->getISurface();
+ }
+};
+};
+
+int main(int argc, char** argv)
+{
+ // set up the thread-pool
+ sp<ProcessState> proc(ProcessState::self());
+ ProcessState::self()->startThreadPool();
+
+ // create a client to surfaceflinger
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+
+ // create pushbuffer surface
+ sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240,
+ PIXEL_FORMAT_RGB_565);
+
+
+ client->openTransaction();
+ surface->setLayer(100000);
+ client->closeTransaction();
+
+ Surface::SurfaceInfo info;
+ surface->lock(&info);
+ ssize_t bpr = info.s * bytesPerPixel(info.format);
+ android_memset16((uint16_t*)info.bits, 0xF800, bpr*info.h);
+ surface->unlockAndPost();
+
+ surface->lock(&info);
+ android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h);
+ surface->unlockAndPost();
+
+ client->openTransaction();
+ surface->setSize(320, 240);
+ client->closeTransaction();
+
+
+ IPCThreadState::self()->joinThreadPool();
+
+ return 0;
+}