summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorJosh Guilfoyle <Josh.Guilfoyle@T-Mobile.com>2010-12-23 14:30:28 -0800
committerJosh Guilfoyle <Josh.Guilfoyle@T-Mobile.com>2010-12-23 22:33:16 -0800
commitce9c0447f946d8fe20d020f01b6548c815579848 (patch)
tree27f4eac06f8960e31f9dfb670f4e67f11029e486 /libs
parent0e7ab2f533037698b4ab4255828fa2e08f90566f (diff)
parent6bcc7a7e5fc5a2340c4f060141bec9d181454807 (diff)
downloadframeworks_base-ce9c0447f946d8fe20d020f01b6548c815579848.zip
frameworks_base-ce9c0447f946d8fe20d020f01b6548c815579848.tar.gz
frameworks_base-ce9c0447f946d8fe20d020f01b6548c815579848.tar.bz2
Merge commit 'android-2.3.1_r1' into themes-exp-2.3.1_r1
Conflicts: core/java/android/app/ActivityThread.java core/java/android/content/pm/ApplicationInfo.java core/java/android/content/pm/PackageParser.java core/res/AndroidManifest.xml services/java/com/android/server/PackageManagerService.java services/java/com/android/server/SystemServer.java test-runner/src/android/test/mock/MockPackageManager.java
Diffstat (limited to 'libs')
-rw-r--r--libs/audioflinger/A2dpAudioInterface.cpp466
-rw-r--r--libs/audioflinger/A2dpAudioInterface.h135
-rw-r--r--libs/audioflinger/Android.mk130
-rw-r--r--libs/audioflinger/AudioBufferProvider.h49
-rw-r--r--libs/audioflinger/AudioDumpInterface.cpp531
-rw-r--r--libs/audioflinger/AudioDumpInterface.h166
-rw-r--r--libs/audioflinger/AudioFlinger.cpp4055
-rw-r--r--libs/audioflinger/AudioFlinger.h807
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.cpp411
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.h151
-rw-r--r--libs/audioflinger/AudioHardwareInterface.cpp182
-rw-r--r--libs/audioflinger/AudioHardwareStub.cpp209
-rw-r--r--libs/audioflinger/AudioHardwareStub.h106
-rw-r--r--libs/audioflinger/AudioMixer.cpp915
-rw-r--r--libs/audioflinger/AudioMixer.h193
-rw-r--r--libs/audioflinger/AudioPolicyManagerBase.cpp1972
-rw-r--r--libs/audioflinger/AudioPolicyService.cpp924
-rw-r--r--libs/audioflinger/AudioPolicyService.h223
-rw-r--r--libs/audioflinger/AudioResampler.cpp595
-rw-r--r--libs/audioflinger/AudioResampler.h93
-rw-r--r--libs/audioflinger/AudioResamplerCubic.cpp184
-rw-r--r--libs/audioflinger/AudioResamplerCubic.h68
-rw-r--r--libs/audioflinger/AudioResamplerSinc.cpp358
-rw-r--r--libs/audioflinger/AudioResamplerSinc.h88
-rw-r--r--libs/binder/IPCThreadState.cpp45
-rw-r--r--libs/binder/IPermissionController.cpp8
-rw-r--r--libs/binder/IServiceManager.cpp9
-rw-r--r--libs/binder/Parcel.cpp50
-rw-r--r--libs/camera/Camera.cpp26
-rw-r--r--libs/camera/CameraParameters.cpp99
-rw-r--r--libs/camera/ICameraService.cpp41
-rw-r--r--libs/gui/Android.mk25
-rw-r--r--libs/gui/ISensorEventConnection.cpp111
-rw-r--r--libs/gui/ISensorServer.cpp102
-rw-r--r--libs/gui/Sensor.cpp185
-rw-r--r--libs/gui/SensorChannel.cpp93
-rw-r--r--libs/gui/SensorEventQueue.cpp149
-rw-r--r--libs/gui/SensorManager.cpp87
-rw-r--r--libs/rs/Android.mk3
-rw-r--r--libs/rs/rs.spec2
-rw-r--r--libs/rs/rsContext.cpp18
-rw-r--r--libs/rs/rsContext.h4
-rw-r--r--libs/rs/rsProgram.cpp2
-rw-r--r--libs/rs/rsScriptC.cpp6
-rw-r--r--libs/storage/Android.mk20
-rw-r--r--libs/storage/IMountService.cpp512
-rw-r--r--libs/storage/IMountServiceListener.cpp53
-rw-r--r--libs/storage/IMountShutdownObserver.cpp43
-rw-r--r--libs/storage/IObbActionListener.cpp60
-rw-r--r--libs/storage/MODULE_LICENSE_APACHE2 (renamed from libs/surfaceflinger/MODULE_LICENSE_APACHE2)0
-rw-r--r--libs/storage/NOTICE190
-rw-r--r--libs/surfaceflinger/Android.mk51
-rw-r--r--libs/surfaceflinger/Barrier.h59
-rw-r--r--libs/surfaceflinger/BlurFilter.cpp376
-rw-r--r--libs/surfaceflinger/BlurFilter.h35
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp364
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.h118
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp401
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h96
-rw-r--r--libs/surfaceflinger/Layer.cpp630
-rw-r--r--libs/surfaceflinger/Layer.h134
-rw-r--r--libs/surfaceflinger/LayerBase.cpp829
-rw-r--r--libs/surfaceflinger/LayerBase.h389
-rw-r--r--libs/surfaceflinger/LayerBlur.cpp265
-rw-r--r--libs/surfaceflinger/LayerBlur.h69
-rw-r--r--libs/surfaceflinger/LayerBuffer.cpp727
-rw-r--r--libs/surfaceflinger/LayerBuffer.h225
-rw-r--r--libs/surfaceflinger/LayerDim.cpp168
-rw-r--r--libs/surfaceflinger/LayerDim.h60
-rw-r--r--libs/surfaceflinger/MessageQueue.cpp192
-rw-r--r--libs/surfaceflinger/MessageQueue.h127
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp1940
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.h420
-rw-r--r--libs/surfaceflinger/Tokenizer.cpp173
-rw-r--r--libs/surfaceflinger/Tokenizer.h57
-rw-r--r--libs/surfaceflinger/Transform.cpp392
-rw-r--r--libs/surfaceflinger/Transform.h128
-rw-r--r--libs/surfaceflinger/clz.cpp37
-rw-r--r--libs/surfaceflinger/clz.h37
-rw-r--r--libs/surfaceflinger/tests/overlays/overlays.cpp59
-rw-r--r--libs/surfaceflinger/tests/resize/resize.cpp62
-rw-r--r--libs/surfaceflinger_client/Android.mk2
-rw-r--r--libs/surfaceflinger_client/ISurface.cpp30
-rw-r--r--libs/surfaceflinger_client/ISurfaceComposer.cpp82
-rw-r--r--libs/surfaceflinger_client/ISurfaceComposerClient.cpp (renamed from libs/surfaceflinger_client/ISurfaceFlingerClient.cpp)55
-rw-r--r--libs/surfaceflinger_client/SharedBufferStack.cpp466
-rw-r--r--libs/surfaceflinger_client/Surface.cpp649
-rw-r--r--libs/surfaceflinger_client/SurfaceComposerClient.cpp453
-rw-r--r--libs/surfaceflinger_client/tests/Android.mk (renamed from libs/surfaceflinger/tests/Android.mk)0
-rw-r--r--libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk (renamed from libs/surfaceflinger/tests/resize/Android.mk)4
-rw-r--r--libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp279
-rw-r--r--libs/ui/Android.mk16
-rw-r--r--libs/ui/EventHub.cpp629
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp71
-rw-r--r--libs/ui/GraphicBuffer.cpp22
-rw-r--r--libs/ui/GraphicBufferAllocator.cpp19
-rw-r--r--libs/ui/GraphicLog.cpp92
-rw-r--r--libs/ui/Input.cpp215
-rw-r--r--libs/ui/InputDispatcher.cpp3504
-rw-r--r--libs/ui/InputManager.cpp83
-rw-r--r--libs/ui/InputReader.cpp3478
-rw-r--r--libs/ui/InputTransport.cpp702
-rw-r--r--libs/ui/PixelFormat.cpp10
-rw-r--r--libs/ui/Rect.cpp8
-rw-r--r--libs/ui/Region.cpp2
-rw-r--r--libs/ui/tests/Android.mk49
-rw-r--r--libs/ui/tests/InputChannel_test.cpp158
-rw-r--r--libs/ui/tests/InputDispatcher_test.cpp226
-rw-r--r--libs/ui/tests/InputPublisherAndConsumer_test.cpp471
-rw-r--r--libs/ui/tests/InputReader_test.cpp3368
-rw-r--r--libs/ui/tests/region/Android.mk (renamed from libs/surfaceflinger/tests/overlays/Android.mk)7
-rw-r--r--libs/ui/tests/region/region.cpp (renamed from libs/ui/tests/region.cpp)0
-rw-r--r--libs/utils/Android.mk25
-rw-r--r--libs/utils/Asset.cpp74
-rw-r--r--libs/utils/AssetManager.cpp8
-rw-r--r--libs/utils/Looper.cpp596
-rw-r--r--libs/utils/ObbFile.cpp354
-rw-r--r--libs/utils/Pool.cpp37
-rw-r--r--libs/utils/ResourceTypes.cpp7
-rw-r--r--libs/utils/StopWatch.cpp11
-rw-r--r--libs/utils/StreamingZipInflater.cpp226
-rw-r--r--libs/utils/String8.cpp48
-rw-r--r--libs/utils/Threads.cpp46
-rw-r--r--libs/utils/VectorImpl.cpp18
-rw-r--r--libs/utils/ZipFileCRO.cpp4
-rw-r--r--libs/utils/ZipFileRO.cpp557
-rw-r--r--libs/utils/tests/Android.mk45
-rw-r--r--libs/utils/tests/Looper_test.cpp425
-rw-r--r--libs/utils/tests/ObbFile_test.cpp100
-rw-r--r--libs/utils/tests/String8_test.cpp75
-rw-r--r--libs/utils/tests/TestHelpers.h79
131 files changed, 18642 insertions, 22817 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
deleted file mode 100644
index 995e31c..0000000
--- a/libs/audioflinger/A2dpAudioInterface.cpp
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <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/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h
deleted file mode 100644
index 48154f9..0000000
--- a/libs/audioflinger/A2dpAudioInterface.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef 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/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
deleted file mode 100644
index 870c0b8..0000000
--- a/libs/audioflinger/Android.mk
+++ /dev/null
@@ -1,130 +0,0 @@
-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/libs/audioflinger/AudioBufferProvider.h b/libs/audioflinger/AudioBufferProvider.h
deleted file mode 100644
index 81c5c39..0000000
--- a/libs/audioflinger/AudioBufferProvider.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp
deleted file mode 100644
index a018b4c..0000000
--- a/libs/audioflinger/AudioDumpInterface.cpp
+++ /dev/null
@@ -1,531 +0,0 @@
-/* //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/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h
deleted file mode 100644
index 4c62b3e..0000000
--- a/libs/audioflinger/AudioDumpInterface.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/* //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/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
deleted file mode 100644
index 2414e8d..0000000
--- a/libs/audioflinger/AudioFlinger.cpp
+++ /dev/null
@@ -1,4055 +0,0 @@
-/* //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/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
deleted file mode 100644
index 739ec33..0000000
--- a/libs/audioflinger/AudioFlinger.h
+++ /dev/null
@@ -1,807 +0,0 @@
-/* //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/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp
deleted file mode 100644
index d63c031..0000000
--- a/libs/audioflinger/AudioHardwareGeneric.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#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/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h
deleted file mode 100644
index aa4e78d..0000000
--- a/libs/audioflinger/AudioHardwareGeneric.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#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/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp
deleted file mode 100644
index 9a4a7f9..0000000
--- a/libs/audioflinger/AudioHardwareInterface.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#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/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp
deleted file mode 100644
index d481150..0000000
--- a/libs/audioflinger/AudioHardwareStub.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/* //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/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h
deleted file mode 100644
index 06a29de..0000000
--- a/libs/audioflinger/AudioHardwareStub.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* //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/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp
deleted file mode 100644
index 19a442a..0000000
--- a/libs/audioflinger/AudioMixer.cpp
+++ /dev/null
@@ -1,915 +0,0 @@
-/* //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/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h
deleted file mode 100644
index 15766cd..0000000
--- a/libs/audioflinger/AudioMixer.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/* //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/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
deleted file mode 100644
index c8b3f48..0000000
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ /dev/null
@@ -1,1972 +0,0 @@
-/*
- * 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/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp
deleted file mode 100644
index bb3905c..0000000
--- a/libs/audioflinger/AudioPolicyService.cpp
+++ /dev/null
@@ -1,924 +0,0 @@
-/*
- * 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/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h
deleted file mode 100644
index a13d0bd..0000000
--- a/libs/audioflinger/AudioPolicyService.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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/libs/audioflinger/AudioResampler.cpp b/libs/audioflinger/AudioResampler.cpp
deleted file mode 100644
index 5dabacb..0000000
--- a/libs/audioflinger/AudioResampler.cpp
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "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/libs/audioflinger/AudioResampler.h b/libs/audioflinger/AudioResampler.h
deleted file mode 100644
index 2dfac76..0000000
--- a/libs/audioflinger/AudioResampler.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/audioflinger/AudioResamplerCubic.cpp b/libs/audioflinger/AudioResamplerCubic.cpp
deleted file mode 100644
index 1d247bd..0000000
--- a/libs/audioflinger/AudioResamplerCubic.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/audioflinger/AudioResamplerCubic.h b/libs/audioflinger/AudioResamplerCubic.h
deleted file mode 100644
index b72b62a..0000000
--- a/libs/audioflinger/AudioResamplerCubic.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/audioflinger/AudioResamplerSinc.cpp b/libs/audioflinger/AudioResamplerSinc.cpp
deleted file mode 100644
index 9e5e254..0000000
--- a/libs/audioflinger/AudioResamplerSinc.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/audioflinger/AudioResamplerSinc.h b/libs/audioflinger/AudioResamplerSinc.h
deleted file mode 100644
index e6cb90b..0000000
--- a/libs/audioflinger/AudioResamplerSinc.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 0016503..13c58f0 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -367,6 +367,26 @@ int64_t IPCThreadState::clearCallingIdentity()
return token;
}
+void IPCThreadState::setStrictModePolicy(int32_t policy)
+{
+ mStrictModePolicy = policy;
+}
+
+int32_t IPCThreadState::getStrictModePolicy() const
+{
+ return mStrictModePolicy;
+}
+
+void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
+{
+ mLastTransactionBinderFlags = flags;
+}
+
+int32_t IPCThreadState::getLastTransactionBinderFlags() const
+{
+ return mLastTransactionBinderFlags;
+}
+
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
@@ -497,12 +517,26 @@ status_t IPCThreadState::transact(int32_t handle,
}
if ((flags & TF_ONE_WAY) == 0) {
+ #if 0
+ if (code == 4) { // relayout
+ LOGI(">>>>>> CALLING transaction 4");
+ } else {
+ LOGI(">>>>>> CALLING transaction %d", code);
+ }
+ #endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
+ #if 0
+ if (code == 4) { // relayout
+ LOGI("<<<<<< RETURNING transaction 4");
+ } else {
+ LOGI("<<<<<< RETURNING transaction %d", code);
+ }
+ #endif
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
@@ -588,7 +622,10 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
}
IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self()), mMyThreadId(androidGetTid())
+ : mProcess(ProcessState::self()),
+ mMyThreadId(androidGetTid()),
+ mStrictModePolicy(0),
+ mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
@@ -972,11 +1009,11 @@ status_t IPCThreadState::executeCommand(int32_t cmd)
}
if (tr.target.ptr) {
sp<BBinder> b((BBinder*)tr.cookie);
- const status_t error = b->transact(tr.code, buffer, &reply, 0);
+ const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
-
+
} else {
- const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);
+ const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
if (error < NO_ERROR) reply.setError(error);
}
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
index bff4c9b..e13036f 100644
--- a/libs/binder/IPermissionController.cpp
+++ b/libs/binder/IPermissionController.cpp
@@ -36,7 +36,7 @@ public:
: BpInterface<IPermissionController>(impl)
{
}
-
+
virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid)
{
Parcel data, reply;
@@ -46,7 +46,7 @@ public:
data.writeInt32(uid);
remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
// fail on exception
- if (reply.readInt32() != 0) return 0;
+ if (reply.readExceptionCode() != 0) return 0;
return reply.readInt32() != 0;
}
};
@@ -66,8 +66,7 @@ status_t BnPermissionController::onTransact(
int32_t pid = data.readInt32();
int32_t uid = data.readInt32();
bool res = checkPermission(permission, pid, uid);
- // write exception
- reply->writeInt32(0);
+ reply->writeNoException();
reply->writeInt32(res ? 1 : 0);
return NO_ERROR;
} break;
@@ -77,4 +76,3 @@ status_t BnPermissionController::onTransact(
}
}; // namespace android
-
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 0cf4158..1fa4c35 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -129,19 +129,19 @@ public:
: BpInterface<IServiceManager>(impl)
{
}
-
+
virtual sp<IBinder> getService(const String16& name) const
{
unsigned n;
for (n = 0; n < 5; n++){
sp<IBinder> svc = checkService(name);
if (svc != NULL) return svc;
- LOGI("Waiting for sevice %s...\n", String8(name).string());
+ LOGI("Waiting for service %s...\n", String8(name).string());
sleep(1);
}
return NULL;
}
-
+
virtual sp<IBinder> checkService( const String16& name) const
{
Parcel data, reply;
@@ -158,7 +158,7 @@ public:
data.writeString16(name);
data.writeStrongBinder(service);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
- return err == NO_ERROR ? reply.readInt32() : err;
+ return err == NO_ERROR ? reply.readExceptionCode() : err;
}
virtual Vector<String16> listServices()
@@ -226,4 +226,3 @@ status_t BnServiceManager::onTransact(
}
}; // namespace android
-
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 00d2210..f329ac4 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -19,6 +19,7 @@
#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
#include <utils/Debug.h>
@@ -47,6 +48,12 @@
#define PAD_SIZE(s) (((s)+3)&~3)
+// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
+#define STRICT_MODE_PENALTY_GATHER 0x100
+
+// Note: must be kept in sync with android/os/Parcel.java's EX_HAS_REPLY_HEADER
+#define EX_HAS_REPLY_HEADER -128
+
// XXX This can be made public if we want to provide
// support for typed data.
struct small_flat_data
@@ -436,19 +443,37 @@ bool Parcel::hasFileDescriptors() const
return mHasFds;
}
+// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
+ writeInt32(IPCThreadState::self()->getStrictModePolicy() |
+ STRICT_MODE_PENALTY_GATHER);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
bool Parcel::checkInterface(IBinder* binder) const
{
- return enforceInterface(binder->getInterfaceDescriptor());
+ return enforceInterface(binder->getInterfaceDescriptor());
}
-bool Parcel::enforceInterface(const String16& interface) const
+bool Parcel::enforceInterface(const String16& interface,
+ IPCThreadState* threadState) const
{
+ int32_t strictPolicy = readInt32();
+ if (threadState == NULL) {
+ threadState = IPCThreadState::self();
+ }
+ if ((threadState->getLastTransactionBinderFlags() &
+ IBinder::FLAG_ONEWAY) != 0) {
+ // For one-way calls, the callee is running entirely
+ // disconnected from the caller, so disable StrictMode entirely.
+ // Not only does disk/network usage not impact the caller, but
+ // there's no way to commuicate back any violations anyway.
+ threadState->setStrictModePolicy(0);
+ } else {
+ threadState->setStrictModePolicy(strictPolicy);
+ }
const String16 str(readString16());
if (str == interface) {
return true;
@@ -457,7 +482,7 @@ bool Parcel::enforceInterface(const String16& interface) const
String8(interface).string(), String8(str).string());
return false;
}
-}
+}
const size_t* Parcel::objects() const
{
@@ -750,6 +775,11 @@ restart_write:
goto restart_write;
}
+status_t Parcel::writeNoException()
+{
+ return writeInt32(0);
+}
+
void Parcel::remove(size_t start, size_t amt)
{
LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -938,6 +968,20 @@ wp<IBinder> Parcel::readWeakBinder() const
return val;
}
+int32_t Parcel::readExceptionCode() const
+{
+ int32_t exception_code = readAligned<int32_t>();
+ if (exception_code == EX_HAS_REPLY_HEADER) {
+ int32_t header_size = readAligned<int32_t>();
+ // Skip over fat responses headers. Not used (or propagated) in
+ // native code
+ setDataPosition(dataPosition() + header_size);
+ // And fat response headers are currently only used when there are no
+ // exceptions, so return no error:
+ return 0;
+ }
+ return exception_code;
+}
native_handle* Parcel::readNativeHandle() const
{
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index f19c502..7efc6d7 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -1,7 +1,6 @@
/*
**
** 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.
@@ -92,16 +91,35 @@ void Camera::init()
Camera::~Camera()
{
- disconnect();
+ // We don't need to call disconnect() here because if the CameraService
+ // thinks we are the owner of the hardware, it will hold a (strong)
+ // reference to us, and we can't possibly be here. We also don't want to
+ // call disconnect() here if we are in the same process as mediaserver,
+ // because we may be invoked by CameraService::Client::connect() and will
+ // deadlock if we call any method of ICamera here.
}
-sp<Camera> Camera::connect()
+int32_t Camera::getNumberOfCameras()
+{
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return 0;
+ return cs->getNumberOfCameras();
+}
+
+status_t Camera::getCameraInfo(int cameraId,
+ struct CameraInfo* cameraInfo) {
+ const sp<ICameraService>& cs = getCameraService();
+ if (cs == 0) return UNKNOWN_ERROR;
+ return cs->getCameraInfo(cameraId, cameraInfo);
+}
+
+sp<Camera> Camera::connect(int cameraId)
{
LOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
- c->mCamera = cs->connect(c);
+ c->mCamera = cs->connect(c, cameraId);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 65fd7ac..83e5e57 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -30,6 +30,8 @@ const char CameraParameters::KEY_PREVIEW_FORMAT[] = "preview-format";
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
@@ -69,8 +71,11 @@ const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
const char CameraParameters::KEY_ZOOM_SUPPORTED[] = "zoom-supported";
const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
+const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
+const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
const char CameraParameters::TRUE[] = "true";
+const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
// Values for white balance settings.
const char CameraParameters::WHITE_BALANCE_AUTO[] = "auto";
@@ -137,6 +142,7 @@ const char CameraParameters::FOCUS_MODE_INFINITY[] = "infinity";
const char CameraParameters::FOCUS_MODE_MACRO[] = "macro";
const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed";
const char CameraParameters::FOCUS_MODE_EDOF[] = "edof";
+const char CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO[] = "continuous-video";
CameraParameters::CameraParameters()
: mMap()
@@ -258,24 +264,57 @@ void CameraParameters::remove(const char *key)
mMap.removeItem(String8(key));
}
-static int parse_size(const char *str, int &width, int &height)
+// Parse string like "640x480" or "10000,20000"
+static int parse_pair(const char *str, int *first, int *second, char delim,
+ char **endptr = NULL)
{
- // Find the width.
+ // Find the first integer.
char *end;
int w = (int)strtol(str, &end, 10);
- // If an 'x' does not immediately follow, give up.
- if (*end != 'x')
+ // If a delimeter does not immediately follow, give up.
+ if (*end != delim) {
+ LOGE("Cannot find delimeter (%c) in str=%s", delim, str);
return -1;
+ }
+
+ // Find the second integer, immediately after the delimeter.
+ int h = (int)strtol(end+1, &end, 10);
- // Find the height, immediately after the 'x'.
- int h = (int)strtol(end+1, 0, 10);
+ *first = w;
+ *second = h;
- width = w;
- height = h;
+ if (endptr) {
+ *endptr = end;
+ }
return 0;
}
+static void parseSizesList(const char *sizesStr, Vector<Size> &sizes)
+{
+ if (sizesStr == 0) {
+ return;
+ }
+
+ char *sizeStartPtr = (char *)sizesStr;
+
+ while (true) {
+ int width, height;
+ int success = parse_pair(sizeStartPtr, &width, &height, 'x',
+ &sizeStartPtr);
+ if (success == -1 || (*sizeStartPtr != ',' && *sizeStartPtr != '\0')) {
+ LOGE("Picture sizes string \"%s\" contains invalid character.", sizesStr);
+ return;
+ }
+ sizes.push(Size(width, height));
+
+ if (*sizeStartPtr == '\0') {
+ return;
+ }
+ sizeStartPtr++;
+ }
+}
+
void CameraParameters::setPreviewSize(int width, int height)
{
char str[32];
@@ -285,19 +324,17 @@ void CameraParameters::setPreviewSize(int width, int height)
void CameraParameters::getPreviewSize(int *width, int *height) const
{
- *width = -1;
- *height = -1;
-
+ *width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
const char *p = get(KEY_PREVIEW_SIZE);
- if (p == 0)
- return;
+ if (p == 0) return;
+ parse_pair(p, width, height, 'x');
+}
- int w, h;
- if (parse_size(p, w, h) == 0) {
- *width = w;
- *height = h;
- }
+void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const
+{
+ const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES);
+ parseSizesList(previewSizesStr, sizes);
}
void CameraParameters::setPreviewFrameRate(int fps)
@@ -310,6 +347,14 @@ int CameraParameters::getPreviewFrameRate() const
return getInt(KEY_PREVIEW_FRAME_RATE);
}
+void CameraParameters::getPreviewFpsRange(int *min_fps, int *max_fps) const
+{
+ *min_fps = *max_fps = -1;
+ const char *p = get(KEY_PREVIEW_FPS_RANGE);
+ if (p == 0) return;
+ parse_pair(p, min_fps, max_fps, ',');
+}
+
void CameraParameters::setPreviewFormat(const char *format)
{
set(KEY_PREVIEW_FORMAT, format);
@@ -329,19 +374,17 @@ void CameraParameters::setPictureSize(int width, int height)
void CameraParameters::getPictureSize(int *width, int *height) const
{
- *width = -1;
- *height = -1;
-
+ *width = *height = -1;
// Get the current string, if it doesn't exist, leave the -1x-1
const char *p = get(KEY_PICTURE_SIZE);
- if (p == 0)
- return;
+ if (p == 0) return;
+ parse_pair(p, width, height, 'x');
+}
- int w, h;
- if (parse_size(p, w, h) == 0) {
- *width = w;
- *height = h;
- }
+void CameraParameters::getSupportedPictureSizes(Vector<Size> &sizes) const
+{
+ const char *pictureSizesStr = get(KEY_SUPPORTED_PICTURE_SIZES);
+ parseSizesList(pictureSizesStr, sizes);
}
void CameraParameters::setPictureFormat(const char *format)
diff --git a/libs/camera/ICameraService.cpp b/libs/camera/ICameraService.cpp
index 46b5478..85f1a29 100644
--- a/libs/camera/ICameraService.cpp
+++ b/libs/camera/ICameraService.cpp
@@ -34,12 +34,34 @@ public:
{
}
+ // get number of cameras available
+ virtual int32_t getNumberOfCameras()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ remote()->transact(BnCameraService::GET_NUMBER_OF_CAMERAS, data, &reply);
+ return reply.readInt32();
+ }
+
+ // get information about a camera
+ virtual status_t getCameraInfo(int cameraId,
+ struct CameraInfo* cameraInfo) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+ data.writeInt32(cameraId);
+ remote()->transact(BnCameraService::GET_CAMERA_INFO, data, &reply);
+ cameraInfo->facing = reply.readInt32();
+ cameraInfo->orientation = reply.readInt32();
+ return reply.readInt32();
+ }
+
// connect to camera service
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient)
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
+ data.writeInt32(cameraId);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
@@ -53,10 +75,25 @@ status_t BnCameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
+ case GET_NUMBER_OF_CAMERAS: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ reply->writeInt32(getNumberOfCameras());
+ return NO_ERROR;
+ } break;
+ case GET_CAMERA_INFO: {
+ CHECK_INTERFACE(ICameraService, data, reply);
+ CameraInfo cameraInfo;
+ memset(&cameraInfo, 0, sizeof(cameraInfo));
+ status_t result = getCameraInfo(data.readInt32(), &cameraInfo);
+ reply->writeInt32(cameraInfo.facing);
+ reply->writeInt32(cameraInfo.orientation);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
- sp<ICamera> camera = connect(cameraClient);
+ sp<ICamera> camera = connect(cameraClient, data.readInt32());
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
new file mode 100644
index 0000000..249558a
--- /dev/null
+++ b/libs/gui/Android.mk
@@ -0,0 +1,25 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ ISensorEventConnection.cpp \
+ ISensorServer.cpp \
+ Sensor.cpp \
+ SensorChannel.cpp \
+ SensorEventQueue.cpp \
+ SensorManager.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libhardware \
+ libhardware_legacy
+
+LOCAL_MODULE:= libgui
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp
new file mode 100644
index 0000000..a5083fe
--- /dev/null
+++ b/libs/gui/ISensorEventConnection.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/ISensorEventConnection.h>
+#include <gui/SensorChannel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+ GET_SENSOR_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+ ENABLE_DISABLE,
+ SET_EVENT_RATE
+};
+
+class BpSensorEventConnection : public BpInterface<ISensorEventConnection>
+{
+public:
+ BpSensorEventConnection(const sp<IBinder>& impl)
+ : BpInterface<ISensorEventConnection>(impl)
+ {
+ }
+
+ virtual sp<SensorChannel> getSensorChannel() const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+ remote()->transact(GET_SENSOR_CHANNEL, data, &reply);
+ return new SensorChannel(reply);
+ }
+
+ virtual status_t enableDisable(int handle, bool enabled)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeInt32(enabled);
+ remote()->transact(ENABLE_DISABLE, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t setEventRate(int handle, nsecs_t ns)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor());
+ data.writeInt32(handle);
+ data.writeInt64(ns);
+ remote()->transact(SET_EVENT_RATE, data, &reply);
+ return reply.readInt32();
+ }
+};
+
+IMPLEMENT_META_INTERFACE(SensorEventConnection, "android.gui.SensorEventConnection");
+
+// ----------------------------------------------------------------------------
+
+status_t BnSensorEventConnection::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_SENSOR_CHANNEL: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ sp<SensorChannel> channel(getSensorChannel());
+ channel->writeToParcel(reply);
+ return NO_ERROR;
+ } break;
+ case ENABLE_DISABLE: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int enabled = data.readInt32();
+ status_t result = enableDisable(handle, enabled);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ case SET_EVENT_RATE: {
+ CHECK_INTERFACE(ISensorEventConnection, data, reply);
+ int handle = data.readInt32();
+ int ns = data.readInt64();
+ status_t result = setEventRate(handle, ns);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ } break;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/ISensorServer.cpp b/libs/gui/ISensorServer.cpp
new file mode 100644
index 0000000..7111092
--- /dev/null
+++ b/libs/gui/ISensorServer.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/Timers.h>
+
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
+
+#include <gui/Sensor.h>
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+ GET_SENSOR_LIST = IBinder::FIRST_CALL_TRANSACTION,
+ CREATE_SENSOR_EVENT_CONNECTION,
+};
+
+class BpSensorServer : public BpInterface<ISensorServer>
+{
+public:
+ BpSensorServer(const sp<IBinder>& impl)
+ : BpInterface<ISensorServer>(impl)
+ {
+ }
+
+ virtual Vector<Sensor> getSensorList()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ remote()->transact(GET_SENSOR_LIST, data, &reply);
+ Sensor s;
+ Vector<Sensor> v;
+ int32_t n = reply.readInt32();
+ v.setCapacity(n);
+ while (n--) {
+ reply.read(static_cast<Flattenable&>(s));
+ v.add(s);
+ }
+ return v;
+ }
+
+ virtual sp<ISensorEventConnection> createSensorEventConnection()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor());
+ remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply);
+ return interface_cast<ISensorEventConnection>(reply.readStrongBinder());
+ }
+};
+
+IMPLEMENT_META_INTERFACE(SensorServer, "android.gui.SensorServer");
+
+// ----------------------------------------------------------------------
+
+status_t BnSensorServer::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_SENSOR_LIST: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ Vector<Sensor> v(getSensorList());
+ size_t n = v.size();
+ reply->writeInt32(n);
+ for (size_t i=0 ; i<n ; i++) {
+ reply->write(static_cast<const Flattenable&>(v[i]));
+ }
+ return NO_ERROR;
+ } break;
+ case CREATE_SENSOR_EVENT_CONNECTION: {
+ CHECK_INTERFACE(ISensorServer, data, reply);
+ sp<ISensorEventConnection> connection(createSensorEventConnection());
+ reply->writeStrongBinder(connection->asBinder());
+ return NO_ERROR;
+ } break;
+ }
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
new file mode 100644
index 0000000..b1f37ff
--- /dev/null
+++ b/libs/gui/Sensor.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Flattenable.h>
+
+#include <hardware/sensors.h>
+
+#include <gui/Sensor.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+Sensor::Sensor()
+ : mHandle(0), mType(0),
+ mMinValue(0), mMaxValue(0), mResolution(0),
+ mPower(0), mMinDelay(0)
+{
+}
+
+Sensor::Sensor(struct sensor_t const* hwSensor)
+{
+ mName = hwSensor->name;
+ mVendor = hwSensor->vendor;
+ mHandle = hwSensor->handle;
+ mType = hwSensor->type;
+ mMinValue = 0; // FIXME: minValue
+ mMaxValue = hwSensor->maxRange; // FIXME: maxValue
+ mResolution = hwSensor->resolution;
+ mPower = hwSensor->power;
+ mMinDelay = hwSensor->minDelay;
+}
+
+Sensor::~Sensor()
+{
+}
+
+const String8& Sensor::getName() const {
+ return mName;
+}
+
+const String8& Sensor::getVendor() const {
+ return mVendor;
+}
+
+int32_t Sensor::getHandle() const {
+ return mHandle;
+}
+
+int32_t Sensor::getType() const {
+ return mType;
+}
+
+float Sensor::getMinValue() const {
+ return mMinValue;
+}
+
+float Sensor::getMaxValue() const {
+ return mMaxValue;
+}
+
+float Sensor::getResolution() const {
+ return mResolution;
+}
+
+float Sensor::getPowerUsage() const {
+ return mPower;
+}
+
+int32_t Sensor::getMinDelay() const {
+ return mMinDelay;
+}
+
+size_t Sensor::getFlattenedSize() const
+{
+ return sizeof(int32_t) + ((mName.length() + 3) & ~3) +
+ sizeof(int32_t) + ((mVendor.length() + 3) & ~3) +
+ sizeof(int32_t) * 2 +
+ sizeof(float) * 4 +
+ sizeof(int32_t);
+}
+
+size_t Sensor::getFdCount() const
+{
+ return 0;
+}
+
+static inline
+size_t write(void* buffer, size_t offset, const String8& value) {
+ memcpy(static_cast<char*>(buffer) + offset, value.string(), value.length());
+ return (value.length() + 3) & ~3;
+}
+
+static inline
+size_t write(void* buffer, size_t offset, float value) {
+ *reinterpret_cast<float*>(static_cast<char*>(buffer) + offset) = value;
+ return sizeof(float);
+}
+
+static inline
+size_t write(void* buffer, size_t offset, int32_t value) {
+ *reinterpret_cast<int32_t*>(static_cast<char*>(buffer) + offset) = value;
+ return sizeof(int32_t);
+}
+
+status_t Sensor::flatten(void* buffer, size_t size,
+ int fds[], size_t count) const
+{
+ if (size < Sensor::getFlattenedSize())
+ return -ENOMEM;
+
+ size_t offset = 0;
+ offset += write(buffer, offset, int32_t(mName.length()));
+ offset += write(buffer, offset, mName);
+ offset += write(buffer, offset, int32_t(mVendor.length()));
+ offset += write(buffer, offset, mVendor);
+ offset += write(buffer, offset, mHandle);
+ offset += write(buffer, offset, mType);
+ offset += write(buffer, offset, mMinValue);
+ offset += write(buffer, offset, mMaxValue);
+ offset += write(buffer, offset, mResolution);
+ offset += write(buffer, offset, mPower);
+ offset += write(buffer, offset, mMinDelay);
+
+ return NO_ERROR;
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, String8* value, int32_t len) {
+ value->setTo(static_cast<char const*>(buffer) + offset, len);
+ return (len + 3) & ~3;
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, float* value) {
+ *value = *reinterpret_cast<float const*>(static_cast<char const*>(buffer) + offset);
+ return sizeof(float);
+}
+
+static inline
+size_t read(void const* buffer, size_t offset, int32_t* value) {
+ *value = *reinterpret_cast<int32_t const*>(static_cast<char const*>(buffer) + offset);
+ return sizeof(int32_t);
+}
+
+status_t Sensor::unflatten(void const* buffer, size_t size,
+ int fds[], size_t count)
+{
+ int32_t len;
+ size_t offset = 0;
+ offset += read(buffer, offset, &len);
+ offset += read(buffer, offset, &mName, len);
+ offset += read(buffer, offset, &len);
+ offset += read(buffer, offset, &mVendor, len);
+ offset += read(buffer, offset, &mHandle);
+ offset += read(buffer, offset, &mType);
+ offset += read(buffer, offset, &mMinValue);
+ offset += read(buffer, offset, &mMaxValue);
+ offset += read(buffer, offset, &mResolution);
+ offset += read(buffer, offset, &mPower);
+ offset += read(buffer, offset, &mMinDelay);
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/SensorChannel.cpp b/libs/gui/SensorChannel.cpp
new file mode 100644
index 0000000..147e1c2
--- /dev/null
+++ b/libs/gui/SensorChannel.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 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 <unistd.h>
+#include <fcntl.h>
+
+#include <utils/Errors.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/SensorChannel.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorChannel::SensorChannel()
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ int fds[2];
+ if (pipe(fds) == 0) {
+ mReceiveFd = fds[0];
+ mSendFd = fds[1];
+ fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+ fcntl(mSendFd, F_SETFL, O_NONBLOCK);
+ }
+}
+
+SensorChannel::SensorChannel(const Parcel& data)
+ : mSendFd(-1), mReceiveFd(-1)
+{
+ mReceiveFd = dup(data.readFileDescriptor());
+ fcntl(mReceiveFd, F_SETFL, O_NONBLOCK);
+}
+
+SensorChannel::~SensorChannel()
+{
+ if (mSendFd >= 0)
+ close(mSendFd);
+
+ if (mReceiveFd >= 0)
+ close(mReceiveFd);
+}
+
+int SensorChannel::getFd() const
+{
+ return mReceiveFd;
+}
+
+ssize_t SensorChannel::write(void const* vaddr, size_t size)
+{
+ ssize_t len = ::write(mSendFd, vaddr, size);
+ if (len < 0)
+ return -errno;
+ return len;
+}
+
+ssize_t SensorChannel::read(void* vaddr, size_t size)
+{
+ ssize_t len = ::read(mReceiveFd, vaddr, size);
+ if (len < 0)
+ return -errno;
+ return len;
+}
+
+status_t SensorChannel::writeToParcel(Parcel* reply) const
+{
+ if (mReceiveFd < 0)
+ return -EINVAL;
+
+ status_t result = reply->writeDupFileDescriptor(mReceiveFd);
+ close(mReceiveFd);
+ mReceiveFd = -1;
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp
new file mode 100644
index 0000000..f935524
--- /dev/null
+++ b/libs/gui/SensorEventQueue.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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 "Sensors"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Looper.h>
+
+#include <gui/Sensor.h>
+#include <gui/SensorChannel.h>
+#include <gui/SensorEventQueue.h>
+#include <gui/ISensorEventConnection.h>
+
+#include <android/sensor.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection)
+ : mSensorEventConnection(connection)
+{
+}
+
+SensorEventQueue::~SensorEventQueue()
+{
+}
+
+void SensorEventQueue::onFirstRef()
+{
+ mSensorChannel = mSensorEventConnection->getSensorChannel();
+}
+
+int SensorEventQueue::getFd() const
+{
+ return mSensorChannel->getFd();
+}
+
+ssize_t SensorEventQueue::write(ASensorEvent const* events, size_t numEvents)
+{
+ ssize_t size = mSensorChannel->write(events, numEvents * sizeof(events[0]));
+ if (size >= 0) {
+ if (size % sizeof(events[0])) {
+ // partial write!!! should never happen.
+ return -EINVAL;
+ }
+ // returns number of events written
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
+ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents)
+{
+ ssize_t size = mSensorChannel->read(events, numEvents*sizeof(events[0]));
+ LOGE_IF(size<0 && size!=-EAGAIN,
+ "SensorChannel::read error (%s)", strerror(-size));
+ if (size >= 0) {
+ if (size % sizeof(events[0])) {
+ // partial read!!! should never happen.
+ LOGE("SensorEventQueue partial read (event-size=%u, read=%d)",
+ sizeof(events[0]), int(size));
+ return -EINVAL;
+ }
+ // returns number of events read
+ size /= sizeof(events[0]);
+ }
+ return size;
+}
+
+sp<Looper> SensorEventQueue::getLooper() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mLooper == 0) {
+ mLooper = new Looper(true);
+ mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL);
+ }
+ return mLooper;
+}
+
+status_t SensorEventQueue::waitForEvent() const
+{
+ const int fd = getFd();
+ sp<Looper> looper(getLooper());
+
+ int32_t result;
+ do {
+ result = looper->pollOnce(-1);
+ if (result == ALOOPER_EVENT_ERROR) {
+ LOGE("SensorChannel::waitForEvent error (errno=%d)", errno);
+ result = -EPIPE; // unknown error, so we make up one
+ break;
+ }
+ } while (result != fd);
+
+ return (result == fd) ? status_t(NO_ERROR) : result;
+}
+
+status_t SensorEventQueue::wake() const
+{
+ sp<Looper> looper(getLooper());
+ looper->wake();
+ return NO_ERROR;
+}
+
+status_t SensorEventQueue::enableSensor(Sensor const* sensor) const {
+ return mSensorEventConnection->enableDisable(sensor->getHandle(), true);
+}
+
+status_t SensorEventQueue::disableSensor(Sensor const* sensor) const {
+ return mSensorEventConnection->enableDisable(sensor->getHandle(), false);
+}
+
+status_t SensorEventQueue::enableSensor(int32_t handle, int32_t us) const {
+ status_t err = mSensorEventConnection->enableDisable(handle, true);
+ if (err == NO_ERROR) {
+ mSensorEventConnection->setEventRate(handle, us2ns(us));
+ }
+ return err;
+}
+
+status_t SensorEventQueue::disableSensor(int32_t handle) const {
+ return mSensorEventConnection->enableDisable(handle, false);
+}
+
+status_t SensorEventQueue::setEventRate(Sensor const* sensor, nsecs_t ns) const {
+ return mSensorEventConnection->setEventRate(sensor->getHandle(), ns);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
diff --git a/libs/gui/SensorManager.cpp b/libs/gui/SensorManager.cpp
new file mode 100644
index 0000000..d719efb
--- /dev/null
+++ b/libs/gui/SensorManager.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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 "Sensors"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Singleton.h>
+
+#include <binder/IServiceManager.h>
+
+#include <gui/ISensorServer.h>
+#include <gui/ISensorEventConnection.h>
+#include <gui/Sensor.h>
+#include <gui/SensorManager.h>
+#include <gui/SensorEventQueue.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SensorManager)
+
+SensorManager::SensorManager()
+ : mSensorList(0)
+{
+ const String16 name("sensorservice");
+ while (getService(name, &mSensorServer) != NO_ERROR) {
+ usleep(250000);
+ }
+
+ mSensors = mSensorServer->getSensorList();
+ size_t count = mSensors.size();
+ mSensorList = (Sensor const**)malloc(count * sizeof(Sensor*));
+ for (size_t i=0 ; i<count ; i++) {
+ mSensorList[i] = mSensors.array() + i;
+ }
+}
+
+SensorManager::~SensorManager()
+{
+ free(mSensorList);
+}
+
+ssize_t SensorManager::getSensorList(Sensor const* const** list) const
+{
+ *list = mSensorList;
+ return mSensors.size();
+}
+
+Sensor const* SensorManager::getDefaultSensor(int type)
+{
+ // For now we just return the first sensor of that type we find.
+ // in the future it will make sense to let the SensorService make
+ // that decision.
+ for (size_t i=0 ; i<mSensors.size() ; i++) {
+ if (mSensorList[i]->getType() == type)
+ return mSensorList[i];
+ }
+ return NULL;
+}
+
+sp<SensorEventQueue> SensorManager::createEventQueue()
+{
+ sp<SensorEventQueue> result = new SensorEventQueue(
+ mSensorServer->createSensorEventConnection());
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 98464a0..6f2cd07 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -102,6 +102,9 @@ LOCAL_SRC_FILES:= \
rsType.cpp \
rsVertexArray.cpp
+ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
+ LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
+endif
LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libacc
LOCAL_LDLIBS := -lpthread -ldl
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index cb9937c..5ae8d01 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -29,7 +29,7 @@ ContextResume {
ContextSetSurface {
param uint32_t width
param uint32_t height
- param android_native_window_t *sur
+ param ANativeWindow *sur
}
ContextDump {
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index d8a9a99..92c6619 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -26,6 +26,7 @@
#include <cutils/properties.h>
+#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <GLES2/gl2.h>
@@ -58,7 +59,18 @@ void Context::initEGL(bool useGL2)
mEGL.mNumConfigs = -1;
EGLint configAttribs[128];
EGLint *configAttribsPtr = configAttribs;
- EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE, GL_NONE, EGL_NONE };
+
+#ifdef HAS_CONTEXT_PRIORITY
+#ifdef EGL_IMG_context_priority
+#warning "using EGL_IMG_context_priority"
+ if (mThreadPriority > 0) {
+ context_attribs2[2] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
+ context_attribs2[3] = EGL_CONTEXT_PRIORITY_LOW_IMG;
+ }
+#endif
+#endif
memset(configAttribs, 0, sizeof(configAttribs));
@@ -473,7 +485,7 @@ Context::~Context()
objDestroyOOBDestroy();
}
-void Context::setSurface(uint32_t w, uint32_t h, android_native_window_t *sur)
+void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur)
{
rsAssert(mIsGraphicsContext);
@@ -888,7 +900,7 @@ void rsi_ContextResume(Context *rsc)
rsc->resume();
}
-void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, android_native_window_t *sur)
+void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur)
{
rsc->setSurface(w, h, sur);
}
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 82c3687..709730e 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -98,7 +98,7 @@ public:
void pause();
void resume();
- void setSurface(uint32_t w, uint32_t h, android_native_window_t *sur);
+ void setSurface(uint32_t w, uint32_t h, ANativeWindow *sur);
void setPriority(int32_t p);
void assignName(ObjectBase *obj, const char *name, uint32_t len);
@@ -246,7 +246,7 @@ private:
static void * threadProc(void *);
- android_native_window_t *mWndSurface;
+ ANativeWindow *mWndSurface;
Vector<ObjectBase *> mNames;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 478a6dc..70e2868 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -195,7 +195,7 @@ bool Program::loadShader(Context *rsc, uint32_t type)
if (rsc->props.mLogShaders) {
LOGV("Loading shader type %x, ID %i", type, mShaderID);
- LOGV(mShader.string());
+ LOGV("%s", mShader.string());
}
if (mShaderID) {
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 1f23773..f4d2451 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -159,7 +159,7 @@ void ScriptCState::runCompiler(Context *rsc, ScriptC *s)
ACCchar buf[4096];
ACCsizei len;
accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf);
- LOGE(buf);
+ LOGE("%s", buf);
rsc->setError(RS_ERROR_BAD_SCRIPT, "Error compiling user script.");
return;
}
@@ -345,7 +345,7 @@ void ScriptCState::appendTypes(const Context *rsc, String8 *str)
s.append(e->getName());
s.append("\n\n");
if (rsc->props.mLogScripts) {
- LOGV(s);
+ LOGV("%s", static_cast<const char*>(s));
}
str->append(s);
}
@@ -372,7 +372,7 @@ void ScriptCState::appendTypes(const Context *rsc, String8 *str)
s.append(mSlotNames[ct]);
s.append(";\n");
if (rsc->props.mLogScripts) {
- LOGV(s);
+ LOGV("%s", static_cast<const char*>(s));
}
str->append(s);
}
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
new file mode 100644
index 0000000..1e52fa4
--- /dev/null
+++ b/libs/storage/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ IMountServiceListener.cpp \
+ IMountShutdownObserver.cpp \
+ IObbActionListener.cpp \
+ IMountService.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+ libutils \
+ libbinder
+
+LOCAL_MODULE:= libstorage
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
new file mode 100644
index 0000000..9ff6930
--- /dev/null
+++ b/libs/storage/IMountService.cpp
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2010 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 "IMountService"
+
+#include <storage/IMountService.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+ TRANSACTION_registerListener = IBinder::FIRST_CALL_TRANSACTION,
+ TRANSACTION_unregisterListener,
+ TRANSACTION_isUsbMassStorageConnected,
+ TRANSACTION_setUsbMassStorageEnabled,
+ TRANSACTION_isUsbMassStorageEnabled,
+ TRANSACTION_mountVolume,
+ TRANSACTION_unmountVolume,
+ TRANSACTION_formatVolume,
+ TRANSACTION_getStorageUsers,
+ TRANSACTION_getVolumeState,
+ TRANSACTION_createSecureContainer,
+ TRANSACTION_finalizeSecureContainer,
+ TRANSACTION_destroySecureContainer,
+ TRANSACTION_mountSecureContainer,
+ TRANSACTION_unmountSecureContainer,
+ TRANSACTION_isSecureContainerMounted,
+ TRANSACTION_renameSecureContainer,
+ TRANSACTION_getSecureContainerPath,
+ TRANSACTION_getSecureContainerList,
+ TRANSACTION_shutdown,
+ TRANSACTION_finishMediaUpdate,
+ TRANSACTION_mountObb,
+ TRANSACTION_unmountObb,
+ TRANSACTION_isObbMounted,
+ TRANSACTION_getMountedObbPath,
+};
+
+class BpMountService: public BpInterface<IMountService>
+{
+public:
+ BpMountService(const sp<IBinder>& impl)
+ : BpInterface<IMountService>(impl)
+ {
+ }
+
+ virtual void registerListener(const sp<IMountServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ if (remote()->transact(TRANSACTION_registerListener, data, &reply) != NO_ERROR) {
+ LOGD("registerListener could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("registerListener caught exception %d\n", err);
+ return;
+ }
+ }
+
+ virtual void unregisterListener(const sp<IMountServiceListener>& listener)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeStrongBinder(listener->asBinder());
+ if (remote()->transact(TRANSACTION_unregisterListener, data, &reply) != NO_ERROR) {
+ LOGD("unregisterListener could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("unregisterListener caught exception %d\n", err);
+ return;
+ }
+ }
+
+ virtual bool isUsbMassStorageConnected()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ if (remote()->transact(TRANSACTION_isUsbMassStorageConnected, data, &reply) != NO_ERROR) {
+ LOGD("isUsbMassStorageConnected could not contact remote\n");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("isUsbMassStorageConnected caught exception %d\n", err);
+ return false;
+ }
+ return reply.readInt32() != 0;
+ }
+
+ virtual void setUsbMassStorageEnabled(const bool enable)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeInt32(enable != 0);
+ if (remote()->transact(TRANSACTION_setUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+ LOGD("setUsbMassStorageEnabled could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("setUsbMassStorageEnabled caught exception %d\n", err);
+ return;
+ }
+ }
+
+ virtual bool isUsbMassStorageEnabled()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ if (remote()->transact(TRANSACTION_isUsbMassStorageEnabled, data, &reply) != NO_ERROR) {
+ LOGD("isUsbMassStorageEnabled could not contact remote\n");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("isUsbMassStorageEnabled caught exception %d\n", err);
+ return false;
+ }
+ return reply.readInt32() != 0;
+ }
+
+ int32_t mountVolume(const String16& mountPoint)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(mountPoint);
+ if (remote()->transact(TRANSACTION_mountVolume, data, &reply) != NO_ERROR) {
+ LOGD("mountVolume could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("mountVolume caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t unmountVolume(const String16& mountPoint, const bool force)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(mountPoint);
+ data.writeInt32(force ? 1 : 0);
+ if (remote()->transact(TRANSACTION_unmountVolume, data, &reply) != NO_ERROR) {
+ LOGD("unmountVolume could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("unmountVolume caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t formatVolume(const String16& mountPoint)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(mountPoint);
+ if (remote()->transact(TRANSACTION_formatVolume, data, &reply) != NO_ERROR) {
+ LOGD("formatVolume could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("formatVolume caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t getStorageUsers(const String16& mountPoint, int32_t** users)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(mountPoint);
+ if (remote()->transact(TRANSACTION_getStorageUsers, data, &reply) != NO_ERROR) {
+ LOGD("getStorageUsers could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("getStorageUsers caught exception %d\n", err);
+ return err;
+ }
+ const int32_t numUsers = reply.readInt32();
+ *users = (int32_t*)malloc(sizeof(int32_t)*numUsers);
+ for (int i = 0; i < numUsers; i++) {
+ **users++ = reply.readInt32();
+ }
+ return numUsers;
+ }
+
+ int32_t getVolumeState(const String16& mountPoint)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(mountPoint);
+ if (remote()->transact(TRANSACTION_getVolumeState, data, &reply) != NO_ERROR) {
+ LOGD("getVolumeState could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("getVolumeState caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t createSecureContainer(const String16& id, const int32_t sizeMb, const String16& fstype,
+ const String16& key, const int32_t ownerUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ data.writeInt32(sizeMb);
+ data.writeString16(fstype);
+ data.writeString16(key);
+ data.writeInt32(ownerUid);
+ if (remote()->transact(TRANSACTION_createSecureContainer, data, &reply) != NO_ERROR) {
+ LOGD("createSecureContainer could not contact remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("createSecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t finalizeSecureContainer(const String16& id)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ if (remote()->transact(TRANSACTION_finalizeSecureContainer, data, &reply) != NO_ERROR) {
+ LOGD("finalizeSecureContainer couldn't call remote\n");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("finalizeSecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t destroySecureContainer(const String16& id)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ if (remote()->transact(TRANSACTION_destroySecureContainer, data, &reply) != NO_ERROR) {
+ LOGD("destroySecureContainer couldn't call remote");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("destroySecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t mountSecureContainer(const String16& id, const String16& key, const int32_t ownerUid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ data.writeString16(key);
+ data.writeInt32(ownerUid);
+ if (remote()->transact(TRANSACTION_mountSecureContainer, data, &reply) != NO_ERROR) {
+ LOGD("mountSecureContainer couldn't call remote");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode(); // What to do...
+ if (err < 0) {
+ LOGD("mountSecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ int32_t unmountSecureContainer(const String16& id, const bool force)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ data.writeInt32(force ? 1 : 0);
+ if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+ LOGD("unmountSecureContainer couldn't call remote");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode(); // What to do...
+ if (err < 0) {
+ LOGD("unmountSecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ bool isSecureContainerMounted(const String16& id)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ if (remote()->transact(TRANSACTION_isSecureContainerMounted, data, &reply) != NO_ERROR) {
+ LOGD("isSecureContainerMounted couldn't call remote");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode(); // What to do...
+ if (err < 0) {
+ LOGD("isSecureContainerMounted caught exception %d\n", err);
+ return false;
+ }
+ return reply.readInt32() != 0;
+ }
+
+ int32_t renameSecureContainer(const String16& oldId, const String16& newId)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(oldId);
+ data.writeString16(newId);
+ if (remote()->transact(TRANSACTION_renameSecureContainer, data, &reply) != NO_ERROR) {
+ LOGD("renameSecureContainer couldn't call remote");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode(); // What to do...
+ if (err < 0) {
+ LOGD("renameSecureContainer caught exception %d\n", err);
+ return err;
+ }
+ return reply.readInt32();
+ }
+
+ bool getSecureContainerPath(const String16& id, String16& path)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ if (remote()->transact(TRANSACTION_getSecureContainerPath, data, &reply) != NO_ERROR) {
+ LOGD("getSecureContainerPath couldn't call remote");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode(); // What to do...
+ if (err < 0) {
+ LOGD("getSecureContainerPath caught exception %d\n", err);
+ return false;
+ }
+ path = reply.readString16();
+ return true;
+ }
+
+ int32_t getSecureContainerList(const String16& id, String16*& containers)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(id);
+ if (remote()->transact(TRANSACTION_getSecureContainerList, data, &reply) != NO_ERROR) {
+ LOGD("getSecureContainerList couldn't call remote");
+ return -1;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("getSecureContainerList caught exception %d\n", err);
+ return err;
+ }
+ const int32_t numStrings = reply.readInt32();
+ containers = new String16[numStrings];
+ for (int i = 0; i < numStrings; i++) {
+ containers[i] = reply.readString16();
+ }
+ return numStrings;
+ }
+
+ void shutdown(const sp<IMountShutdownObserver>& observer)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeStrongBinder(observer->asBinder());
+ if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
+ LOGD("shutdown could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("shutdown caught exception %d\n", err);
+ return;
+ }
+ reply.readExceptionCode();
+ }
+
+ void finishMediaUpdate()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ if (remote()->transact(TRANSACTION_finishMediaUpdate, data, &reply) != NO_ERROR) {
+ LOGD("finishMediaUpdate could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("finishMediaUpdate caught exception %d\n", err);
+ return;
+ }
+ reply.readExceptionCode();
+ }
+
+ void mountObb(const String16& filename, const String16& key,
+ const sp<IObbActionListener>& token, int32_t nonce)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(filename);
+ data.writeString16(key);
+ data.writeStrongBinder(token->asBinder());
+ data.writeInt32(nonce);
+ if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
+ LOGD("mountObb could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("mountObb caught exception %d\n", err);
+ return;
+ }
+ }
+
+ void unmountObb(const String16& filename, const bool force,
+ const sp<IObbActionListener>& token, const int32_t nonce)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(filename);
+ data.writeInt32(force ? 1 : 0);
+ data.writeStrongBinder(token->asBinder());
+ data.writeInt32(nonce);
+ if (remote()->transact(TRANSACTION_unmountObb, data, &reply) != NO_ERROR) {
+ LOGD("unmountObb could not contact remote\n");
+ return;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("unmountObb caught exception %d\n", err);
+ return;
+ }
+ }
+
+ bool isObbMounted(const String16& filename)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(filename);
+ if (remote()->transact(TRANSACTION_isObbMounted, data, &reply) != NO_ERROR) {
+ LOGD("isObbMounted could not contact remote\n");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("isObbMounted caught exception %d\n", err);
+ return false;
+ }
+ return reply.readInt32() != 0;
+ }
+
+ bool getMountedObbPath(const String16& filename, String16& path)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
+ data.writeString16(filename);
+ if (remote()->transact(TRANSACTION_getMountedObbPath, data, &reply) != NO_ERROR) {
+ LOGD("getMountedObbPath could not contact remote\n");
+ return false;
+ }
+ int32_t err = reply.readExceptionCode();
+ if (err < 0) {
+ LOGD("getMountedObbPath caught exception %d\n", err);
+ return false;
+ }
+ path = reply.readString16();
+ return true;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(MountService, "IMountService");
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountServiceListener.cpp b/libs/storage/IMountServiceListener.cpp
new file mode 100644
index 0000000..c98a424
--- /dev/null
+++ b/libs/storage/IMountServiceListener.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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 <storage/IMountServiceListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+ TRANSACTION_onUsbMassStorageConnectionChanged = IBinder::FIRST_CALL_TRANSACTION,
+ TRANSACTION_onStorageStateChanged,
+};
+
+status_t BnMountServiceListener::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case TRANSACTION_onUsbMassStorageConnectionChanged: {
+ CHECK_INTERFACE(IMountServiceListener, data, reply);
+ bool connected = (data.readInt32() != 0);
+ onUsbMassStorageConnectionChanged(connected);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case TRANSACTION_onStorageStateChanged: {
+ CHECK_INTERFACE(IMountServiceListener, data, reply);
+ String16 path = data.readString16();
+ String16 oldState = data.readString16();
+ String16 newState = data.readString16();
+ onStorageStateChanged(path, oldState, newState);
+ reply->writeNoException();
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IMountShutdownObserver.cpp b/libs/storage/IMountShutdownObserver.cpp
new file mode 100644
index 0000000..1a6fdee
--- /dev/null
+++ b/libs/storage/IMountShutdownObserver.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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 <storage/IMountShutdownObserver.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+ TRANSACTION_onShutDownComplete = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+status_t BnMountShutdownObserver::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case TRANSACTION_onShutDownComplete: {
+ CHECK_INTERFACE(IMountShutdownObserver, data, reply);
+ int32_t statusCode = data.readInt32();
+ onShutDownComplete(statusCode);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/storage/IObbActionListener.cpp b/libs/storage/IObbActionListener.cpp
new file mode 100644
index 0000000..eaa211e
--- /dev/null
+++ b/libs/storage/IObbActionListener.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 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 <storage/IObbActionListener.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+enum {
+ TRANSACTION_onObbResult = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+// This is a stub that real consumers should override.
+class BpObbActionListener: public BpInterface<IObbActionListener> {
+public:
+ BpObbActionListener(const sp<IBinder>& impl)
+ : BpInterface<IObbActionListener>(impl)
+ { }
+
+ virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { }
+};
+
+IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnObbActionListener::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case TRANSACTION_onObbResult: {
+ CHECK_INTERFACE(IObbActionListener, data, reply);
+ String16 filename = data.readString16();
+ int32_t nonce = data.readInt32();
+ int32_t state = data.readInt32();
+ onObbResult(filename, nonce, state);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------
+
+};
diff --git a/libs/surfaceflinger/MODULE_LICENSE_APACHE2 b/libs/storage/MODULE_LICENSE_APACHE2
index e69de29..e69de29 100644
--- a/libs/surfaceflinger/MODULE_LICENSE_APACHE2
+++ b/libs/storage/MODULE_LICENSE_APACHE2
diff --git a/libs/storage/NOTICE b/libs/storage/NOTICE
new file mode 100644
index 0000000..5d14293
--- /dev/null
+++ b/libs/storage/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2010, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
deleted file mode 100644
index 86eb78d..0000000
--- a/libs/surfaceflinger/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-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/libs/surfaceflinger/Barrier.h b/libs/surfaceflinger/Barrier.h
deleted file mode 100644
index e2bcf6a..0000000
--- a/libs/surfaceflinger/Barrier.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp
deleted file mode 100644
index 1ffbd5b..0000000
--- a/libs/surfaceflinger/BlurFilter.cpp
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
-**
-** 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/libs/surfaceflinger/BlurFilter.h b/libs/surfaceflinger/BlurFilter.h
deleted file mode 100644
index 294db43..0000000
--- a/libs/surfaceflinger/BlurFilter.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-**
-** 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/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
deleted file mode 100644
index ea68352..0000000
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
deleted file mode 100644
index df046af..0000000
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
deleted file mode 100644
index 1d09f84..0000000
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
deleted file mode 100644
index 8369bb8..0000000
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
deleted file mode 100644
index ce7e9aa..0000000
--- a/libs/surfaceflinger/Layer.cpp
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
deleted file mode 100644
index 743afb4..0000000
--- a/libs/surfaceflinger/Layer.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_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/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
deleted file mode 100644
index a8b735e..0000000
--- a/libs/surfaceflinger/LayerBase.cpp
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
deleted file mode 100644
index 62ec839..0000000
--- a/libs/surfaceflinger/LayerBase.h
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_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/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp
deleted file mode 100644
index 5fd7904..0000000
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h
deleted file mode 100644
index 5b63dec..0000000
--- a/libs/surfaceflinger/LayerBlur.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_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/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
deleted file mode 100644
index 5c21593..0000000
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
deleted file mode 100644
index b176623..0000000
--- a/libs/surfaceflinger/LayerBuffer.h
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_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/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp
deleted file mode 100644
index fd61e30..0000000
--- a/libs/surfaceflinger/LayerDim.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h
deleted file mode 100644
index d4672a1..0000000
--- a/libs/surfaceflinger/LayerDim.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_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/libs/surfaceflinger/MessageQueue.cpp b/libs/surfaceflinger/MessageQueue.cpp
deleted file mode 100644
index b43d801..0000000
--- a/libs/surfaceflinger/MessageQueue.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/Log.h>
-#include <binder/IPCThreadState.h>
-
-#include "MessageQueue.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-void MessageList::insert(const sp<MessageBase>& node)
-{
- LIST::iterator cur(mList.begin());
- LIST::iterator end(mList.end());
- while (cur != end) {
- if (*node < **cur) {
- mList.insert(cur, node);
- return;
- }
- ++cur;
- }
- mList.insert(++end, node);
-}
-
-void MessageList::remove(MessageList::LIST::iterator pos)
-{
- mList.erase(pos);
-}
-
-// ---------------------------------------------------------------------------
-
-MessageQueue::MessageQueue()
- : mInvalidate(false)
-{
- mInvalidateMessage = new MessageBase(INVALIDATE);
-}
-
-MessageQueue::~MessageQueue()
-{
-}
-
-MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
-{
- MessageList::value_type result;
-
- bool again;
- do {
- const nsecs_t timeoutTime = systemTime() + timeout;
- while (true) {
- Mutex::Autolock _l(mLock);
- nsecs_t now = systemTime();
- nsecs_t nextEventTime = -1;
-
- // invalidate messages are always handled first
- if (mInvalidate) {
- mInvalidate = false;
- mInvalidateMessage->when = now;
- result = mInvalidateMessage;
- break;
- }
-
- LIST::iterator cur(mMessages.begin());
- if (cur != mMessages.end()) {
- result = *cur;
- }
-
- if (result != 0) {
- if (result->when <= now) {
- // there is a message to deliver
- mMessages.remove(cur);
- break;
- }
- if (timeout>=0 && timeoutTime < now) {
- // we timed-out, return a NULL message
- result = 0;
- break;
- }
- nextEventTime = result->when;
- result = 0;
- }
-
- if (timeout >= 0 && nextEventTime > 0) {
- if (nextEventTime > timeoutTime) {
- nextEventTime = timeoutTime;
- }
- }
-
- if (nextEventTime >= 0) {
- //LOGD("nextEventTime = %lld ms", nextEventTime);
- if (nextEventTime > 0) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- const nsecs_t reltime = nextEventTime - systemTime();
- if (reltime > 0) {
- mCondition.waitRelative(mLock, reltime);
- }
- }
- } else {
- //LOGD("going to wait");
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- mCondition.wait(mLock);
- }
- }
- // here we're not holding the lock anymore
-
- if (result == 0)
- break;
-
- again = result->handler();
- if (again) {
- // the message has been processed. release our reference to it
- // without holding the lock.
- result = 0;
- }
-
- } while (again);
-
- return result;
-}
-
-status_t MessageQueue::postMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
-{
- return queueMessage(message, relTime, flags);
-}
-
-status_t MessageQueue::invalidate() {
- Mutex::Autolock _l(mLock);
- mInvalidate = true;
- mCondition.signal();
- return NO_ERROR;
-}
-
-status_t MessageQueue::queueMessage(
- const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
-{
- Mutex::Autolock _l(mLock);
- message->when = systemTime() + relTime;
- mMessages.insert(message);
-
- //LOGD("MessageQueue::queueMessage time = %lld ms", message->when);
- //dumpLocked(message);
-
- mCondition.signal();
- return NO_ERROR;
-}
-
-void MessageQueue::dump(const MessageList::value_type& message)
-{
- Mutex::Autolock _l(mLock);
- dumpLocked(message);
-}
-
-void MessageQueue::dumpLocked(const MessageList::value_type& message)
-{
- LIST::const_iterator cur(mMessages.begin());
- LIST::const_iterator end(mMessages.end());
- int c = 0;
- while (cur != end) {
- const char tick = (*cur == message) ? '>' : ' ';
- LOGD("%c %d: msg{.what=%08x, when=%lld}",
- tick, c, (*cur)->what, (*cur)->when);
- ++cur;
- c++;
- }
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/surfaceflinger/MessageQueue.h b/libs/surfaceflinger/MessageQueue.h
deleted file mode 100644
index dc8138d..0000000
--- a/libs/surfaceflinger/MessageQueue.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MESSAGE_QUEUE_H
-#define ANDROID_MESSAGE_QUEUE_H
-
-#include <stdint.h>
-#include <errno.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/List.h>
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class MessageBase;
-
-class MessageList
-{
- List< sp<MessageBase> > mList;
- typedef List< sp<MessageBase> > LIST;
-public:
- typedef sp<MessageBase> value_type;
- inline LIST::iterator begin() { return mList.begin(); }
- inline LIST::const_iterator begin() const { return mList.begin(); }
- inline LIST::iterator end() { return mList.end(); }
- inline LIST::const_iterator end() const { return mList.end(); }
- inline bool isEmpty() const { return mList.empty(); }
- void insert(const sp<MessageBase>& node);
- void remove(LIST::iterator pos);
-};
-
-// ============================================================================
-
-class MessageBase :
- public LightRefBase<MessageBase>
-{
-public:
- nsecs_t when;
- uint32_t what;
- int32_t arg0;
-
- MessageBase() : when(0), what(0), arg0(0) { }
- MessageBase(uint32_t what, int32_t arg0=0)
- : when(0), what(what), arg0(arg0) { }
-
- // return true if message has a handler
- virtual bool handler() { return false; }
-
-protected:
- virtual ~MessageBase() { }
-
-private:
- friend class LightRefBase<MessageBase>;
-};
-
-inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
- return lhs.when < rhs.when;
-}
-
-// ---------------------------------------------------------------------------
-
-class MessageQueue
-{
- typedef List< sp<MessageBase> > LIST;
-public:
-
- // this is a work-around the multichar constant warning. A macro would
- // work too, but would pollute the namespace.
- template <int a, int b, int c, int d>
- struct WHAT {
- static const uint32_t Value =
- (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)|
- (uint32_t(c&0xff)<<8)|uint32_t(d&0xff);
- };
-
- MessageQueue();
- ~MessageQueue();
-
- // pre-defined messages
- enum {
- INVALIDATE = WHAT<'_','p','d','t'>::Value
- };
-
- MessageList::value_type waitMessage(nsecs_t timeout = -1);
-
- status_t postMessage(const MessageList::value_type& message,
- nsecs_t reltime=0, uint32_t flags = 0);
-
- status_t invalidate();
-
- void dump(const MessageList::value_type& message);
-
-private:
- status_t queueMessage(const MessageList::value_type& message,
- nsecs_t reltime, uint32_t flags);
- void dumpLocked(const MessageList::value_type& message);
-
- Mutex mLock;
- Condition mCondition;
- MessageList mMessages;
- bool mInvalidate;
- MessageList::value_type mInvalidateMessage;
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
deleted file mode 100644
index 0722fda..0000000
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ /dev/null
@@ -1,1940 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
deleted file mode 100644
index d75dc15..0000000
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp
deleted file mode 100644
index be3a239..0000000
--- a/libs/surfaceflinger/Tokenizer.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/Tokenizer.h b/libs/surfaceflinger/Tokenizer.h
deleted file mode 100644
index 6b3057d..0000000
--- a/libs/surfaceflinger/Tokenizer.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp
deleted file mode 100644
index 175f989..0000000
--- a/libs/surfaceflinger/Transform.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h
deleted file mode 100644
index 2e5b893..0000000
--- a/libs/surfaceflinger/Transform.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/clz.cpp b/libs/surfaceflinger/clz.cpp
deleted file mode 100644
index 2456b86..0000000
--- a/libs/surfaceflinger/clz.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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/libs/surfaceflinger/clz.h b/libs/surfaceflinger/clz.h
deleted file mode 100644
index 0ddf986..0000000
--- a/libs/surfaceflinger/clz.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_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/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp
deleted file mode 100644
index c248a61..0000000
--- a/libs/surfaceflinger/tests/overlays/overlays.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#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/libs/surfaceflinger/tests/resize/resize.cpp b/libs/surfaceflinger/tests/resize/resize.cpp
deleted file mode 100644
index 127cca3..0000000
--- a/libs/surfaceflinger/tests/resize/resize.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#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;
-}
diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk
index fe85b34..ce3c71a 100644
--- a/libs/surfaceflinger_client/Android.mk
+++ b/libs/surfaceflinger_client/Android.mk
@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
ISurfaceComposer.cpp \
ISurface.cpp \
- ISurfaceFlingerClient.cpp \
+ ISurfaceComposerClient.cpp \
LayerState.cpp \
SharedBufferStack.cpp \
Surface.cpp \
diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp
index bb86199..7049d9e 100644
--- a/libs/surfaceflinger_client/ISurface.cpp
+++ b/libs/surfaceflinger_client/ISurface.cpp
@@ -71,11 +71,15 @@ public:
{
}
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage)
+ virtual sp<GraphicBuffer> requestBuffer(int bufferIdx,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
{
Parcel data, reply;
data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
+ data.writeInt32(w);
+ data.writeInt32(h);
+ data.writeInt32(format);
data.writeInt32(usage);
remote()->transact(REQUEST_BUFFER, data, &reply);
sp<GraphicBuffer> buffer = new GraphicBuffer();
@@ -83,6 +87,16 @@ public:
return buffer;
}
+ virtual status_t setBufferCount(int bufferCount)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
+ data.writeInt32(bufferCount);
+ remote()->transact(SET_BUFFER_COUNT, data, &reply);
+ status_t err = reply.readInt32();
+ return err;
+ }
+
virtual status_t registerBuffers(const BufferHeap& buffers)
{
Parcel data, reply;
@@ -140,12 +154,22 @@ status_t BnSurface::onTransact(
case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurface, data, reply);
int bufferIdx = data.readInt32();
- int usage = data.readInt32();
- sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage));
+ uint32_t w = data.readInt32();
+ uint32_t h = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t usage = data.readInt32();
+ sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage));
if (buffer == NULL)
return BAD_VALUE;
return reply->write(*buffer);
}
+ case SET_BUFFER_COUNT: {
+ CHECK_INTERFACE(ISurface, data, reply);
+ int bufferCount = data.readInt32();
+ status_t err = setBufferCount(bufferCount);
+ reply->writeInt32(err);
+ return NO_ERROR;
+ }
case REGISTER_BUFFERS: {
CHECK_INTERFACE(ISurface, data, reply);
BufferHeap buffer;
diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp
index b6f4e24..969ee79 100644
--- a/libs/surfaceflinger_client/ISurfaceComposer.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp
@@ -46,13 +46,22 @@ public:
{
}
- virtual sp<ISurfaceFlingerClient> createConnection()
+ virtual sp<ISurfaceComposerClient> createConnection()
{
uint32_t n;
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
- return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder());
+ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
+ }
+
+ virtual sp<ISurfaceComposerClient> createClientConnection()
+ {
+ uint32_t n;
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply);
+ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
}
virtual sp<IMemoryHeap> getCblk() const
@@ -115,6 +124,42 @@ public:
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
+ virtual status_t captureScreen(DisplayID dpy,
+ sp<IMemoryHeap>* heap,
+ uint32_t* width, uint32_t* height, PixelFormat* format,
+ uint32_t reqWidth, uint32_t reqHeight)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeInt32(dpy);
+ data.writeInt32(reqWidth);
+ data.writeInt32(reqHeight);
+ remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
+ *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
+ *width = reply.readInt32();
+ *height = reply.readInt32();
+ *format = reply.readInt32();
+ return reply.readInt32();
+ }
+
+ virtual status_t turnElectronBeamOff(int32_t mode)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_OFF, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t turnElectronBeamOn(int32_t mode)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ remote()->transact(BnSurfaceComposer::TURN_ELECTRON_BEAM_ON, data, &reply);
+ return reply.readInt32();
+ }
+
virtual void signal() const
{
Parcel data, reply;
@@ -136,6 +181,11 @@ status_t BnSurfaceComposer::onTransact(
sp<IBinder> b = createConnection()->asBinder();
reply->writeStrongBinder(b);
} break;
+ case CREATE_CLIENT_CONNECTION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> b = createClientConnection()->asBinder();
+ reply->writeStrongBinder(b);
+ } break;
case OPEN_GLOBAL_TRANSACTION: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
openGlobalTransaction();
@@ -176,6 +226,34 @@ status_t BnSurfaceComposer::onTransact(
sp<IBinder> b = getCblk()->asBinder();
reply->writeStrongBinder(b);
} break;
+ case CAPTURE_SCREEN: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ DisplayID dpy = data.readInt32();
+ uint32_t reqWidth = data.readInt32();
+ uint32_t reqHeight = data.readInt32();
+ sp<IMemoryHeap> heap;
+ uint32_t w, h;
+ PixelFormat f;
+ status_t res = captureScreen(dpy, &heap, &w, &h, &f,
+ reqWidth, reqHeight);
+ reply->writeStrongBinder(heap->asBinder());
+ reply->writeInt32(w);
+ reply->writeInt32(h);
+ reply->writeInt32(f);
+ reply->writeInt32(res);
+ } break;
+ case TURN_ELECTRON_BEAM_OFF: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t mode = data.readInt32();
+ status_t res = turnElectronBeamOff(mode);
+ reply->writeInt32(res);
+ }
+ case TURN_ELECTRON_BEAM_ON: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ int32_t mode = data.readInt32();
+ status_t res = turnElectronBeamOn(mode);
+ reply->writeInt32(res);
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
index def96d7..2cc1f8e 100644
--- a/libs/surfaceflinger_client/ISurfaceFlingerClient.cpp
+++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp
@@ -30,7 +30,7 @@
#include <ui/Rect.h>
#include <surfaceflinger/ISurface.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include <private/surfaceflinger/LayerState.h>
// ---------------------------------------------------------------------------
@@ -51,27 +51,37 @@ namespace android {
enum {
GET_CBLK = IBinder::FIRST_CALL_TRANSACTION,
+ GET_TOKEN,
CREATE_SURFACE,
DESTROY_SURFACE,
SET_STATE
};
-class BpSurfaceFlingerClient : public BpInterface<ISurfaceFlingerClient>
+class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
{
public:
- BpSurfaceFlingerClient(const sp<IBinder>& impl)
- : BpInterface<ISurfaceFlingerClient>(impl)
+ BpSurfaceComposerClient(const sp<IBinder>& impl)
+ : BpInterface<ISurfaceComposerClient>(impl)
{
}
virtual sp<IMemoryHeap> getControlBlock() const
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor());
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
remote()->transact(GET_CBLK, data, &reply);
return interface_cast<IMemoryHeap>(reply.readStrongBinder());
}
+ virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ data.writeStrongBinder(sur->asBinder());
+ remote()->transact(GET_TOKEN, data, &reply);
+ return reply.readInt32();
+ }
+
virtual sp<ISurface> createSurface( surface_data_t* params,
int pid,
const String8& name,
@@ -82,7 +92,7 @@ public:
uint32_t flags)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor());
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
data.writeInt32(pid);
data.writeString8(name);
data.writeInt32(display);
@@ -94,11 +104,11 @@ public:
params->readFromParcel(reply);
return interface_cast<ISurface>(reply.readStrongBinder());
}
-
+
virtual status_t destroySurface(SurfaceID sid)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor());
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
data.writeInt32(sid);
remote()->transact(DESTROY_SURFACE, data, &reply);
return reply.readInt32();
@@ -107,7 +117,7 @@ public:
virtual status_t setState(int32_t count, const layer_state_t* states)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor());
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
data.writeInt32(count);
for (int i=0 ; i<count ; i++)
states[i].write(data);
@@ -116,26 +126,33 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient");
+IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient");
// ----------------------------------------------------------------------
-status_t BnSurfaceFlingerClient::onTransact(
+status_t BnSurfaceComposerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// codes that don't require permission check
switch(code) {
case GET_CBLK: {
- CHECK_INTERFACE(ISurfaceFlingerClient, data, reply);
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
sp<IMemoryHeap> ctl(getControlBlock());
reply->writeStrongBinder(ctl->asBinder());
return NO_ERROR;
} break;
+ case GET_TOKEN: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder());
+ ssize_t token = getTokenForSurface(sur);
+ reply->writeInt32(token);
+ return NO_ERROR;
+ } break;
}
// these must be checked
-
+
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
@@ -150,10 +167,10 @@ status_t BnSurfaceFlingerClient::onTransact(
return PERMISSION_DENIED;
}
}
-
+
switch(code) {
case CREATE_SURFACE: {
- CHECK_INTERFACE(ISurfaceFlingerClient, data, reply);
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
surface_data_t params;
int32_t pid = data.readInt32();
String8 name = data.readString8();
@@ -169,12 +186,12 @@ status_t BnSurfaceFlingerClient::onTransact(
return NO_ERROR;
} break;
case DESTROY_SURFACE: {
- CHECK_INTERFACE(ISurfaceFlingerClient, data, reply);
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
reply->writeInt32( destroySurface( data.readInt32() ) );
return NO_ERROR;
} break;
case SET_STATE: {
- CHECK_INTERFACE(ISurfaceFlingerClient, data, reply);
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
int32_t count = data.readInt32();
layer_state_t* states = new layer_state_t[count];
for (int i=0 ; i<count ; i++)
@@ -191,7 +208,7 @@ status_t BnSurfaceFlingerClient::onTransact(
// ----------------------------------------------------------------------
-status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel)
+status_t ISurfaceComposerClient::surface_data_t::readFromParcel(const Parcel& parcel)
{
token = parcel.readInt32();
identity = parcel.readInt32();
@@ -201,7 +218,7 @@ status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& par
return NO_ERROR;
}
-status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) const
+status_t ISurfaceComposerClient::surface_data_t::writeToParcel(Parcel* parcel) const
{
parcel->writeInt32(token);
parcel->writeInt32(identity);
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index a17e8ac..4bc5d9e 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -44,15 +44,11 @@ SharedClient::~SharedClient() {
// these functions are used by the clients
status_t SharedClient::validate(size_t i) const {
- if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX))
+ if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX))
return BAD_INDEX;
return surfaces[i].status;
}
-uint32_t SharedClient::getIdentity(size_t token) const {
- return uint32_t(surfaces[token].identity);
-}
-
// ----------------------------------------------------------------------------
@@ -62,24 +58,60 @@ SharedBufferStack::SharedBufferStack()
void SharedBufferStack::init(int32_t i)
{
- inUse = -1;
+ inUse = -2;
status = NO_ERROR;
identity = i;
}
+status_t SharedBufferStack::setCrop(int buffer, const Rect& crop)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+
+ buffers[buffer].crop.l = uint16_t(crop.left);
+ buffers[buffer].crop.t = uint16_t(crop.top);
+ buffers[buffer].crop.r = uint16_t(crop.right);
+ buffers[buffer].crop.b = uint16_t(crop.bottom);
+ return NO_ERROR;
+}
+
+status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+ buffers[buffer].transform = transform;
+ return NO_ERROR;
+}
+
status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
{
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return BAD_INDEX;
- // in the current implementation we only send a single rectangle
- const Rect bounds(dirty.getBounds());
- FlatRegion& reg(dirtyRegion[buffer]);
- reg.count = 1;
- reg.rects[0] = uint16_t(bounds.left);
- reg.rects[1] = uint16_t(bounds.top);
- reg.rects[2] = uint16_t(bounds.right);
- reg.rects[3] = uint16_t(bounds.bottom);
+ FlatRegion& reg(buffers[buffer].dirtyRegion);
+ if (dirty.isEmpty()) {
+ reg.count = 0;
+ return NO_ERROR;
+ }
+
+ size_t count;
+ Rect const* r = dirty.getArray(&count);
+ if (count > FlatRegion::NUM_RECT_MAX) {
+ const Rect bounds(dirty.getBounds());
+ reg.count = 1;
+ reg.rects[0].l = uint16_t(bounds.left);
+ reg.rects[0].t = uint16_t(bounds.top);
+ reg.rects[0].r = uint16_t(bounds.right);
+ reg.rects[0].b = uint16_t(bounds.bottom);
+ } else {
+ reg.count = count;
+ for (size_t i=0 ; i<count ; i++) {
+ reg.rects[i].l = uint16_t(r[i].left);
+ reg.rects[i].t = uint16_t(r[i].top);
+ reg.rects[i].r = uint16_t(r[i].right);
+ reg.rects[i].b = uint16_t(r[i].bottom);
+ }
+ }
return NO_ERROR;
}
@@ -89,18 +121,57 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const
if (uint32_t(buffer) >= NUM_BUFFER_MAX)
return res;
- const FlatRegion& reg(dirtyRegion[buffer]);
- res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3]));
+ const FlatRegion& reg(buffers[buffer].dirtyRegion);
+ if (reg.count > FlatRegion::NUM_RECT_MAX)
+ return res;
+
+ if (reg.count == 1) {
+ const Rect r(
+ reg.rects[0].l,
+ reg.rects[0].t,
+ reg.rects[0].r,
+ reg.rects[0].b);
+ res.set(r);
+ } else {
+ for (size_t i=0 ; i<reg.count ; i++) {
+ const Rect r(
+ reg.rects[i].l,
+ reg.rects[i].t,
+ reg.rects[i].r,
+ reg.rects[i].b);
+ res.orSelf(r);
+ }
+ }
+ return res;
+}
+
+Rect SharedBufferStack::getCrop(int buffer) const
+{
+ Rect res(-1, -1);
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return res;
+ res.left = buffers[buffer].crop.l;
+ res.top = buffers[buffer].crop.t;
+ res.right = buffers[buffer].crop.r;
+ res.bottom = buffers[buffer].crop.b;
return res;
}
+uint32_t SharedBufferStack::getTransform(int buffer) const
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return 0;
+ return buffers[buffer].transform;
+}
+
+
// ----------------------------------------------------------------------------
SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
- int surface, int num, int32_t identity)
+ int surface, int32_t identity)
: mSharedClient(sharedClient),
mSharedStack(sharedClient->surfaces + surface),
- mNumBuffers(num), mIdentity(identity)
+ mIdentity(identity)
{
}
@@ -108,22 +179,16 @@ SharedBufferBase::~SharedBufferBase()
{
}
-uint32_t SharedBufferBase::getIdentity()
-{
- SharedBufferStack& stack( *mSharedStack );
- return stack.identity;
-}
-
status_t SharedBufferBase::getStatus() const
{
SharedBufferStack& stack( *mSharedStack );
return stack.status;
}
-size_t SharedBufferBase::getFrontBuffer() const
+int32_t SharedBufferBase::getIdentity() const
{
SharedBufferStack& stack( *mSharedStack );
- return size_t( stack.head );
+ return stack.identity;
}
String8 SharedBufferBase::dump(char const* prefix) const
@@ -132,16 +197,52 @@ String8 SharedBufferBase::dump(char const* prefix) const
char buffer[SIZE];
String8 result;
SharedBufferStack& stack( *mSharedStack );
- int tail = (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
snprintf(buffer, SIZE,
- "%s[ head=%2d, available=%2d, queued=%2d, tail=%2d ] "
- "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
- prefix, stack.head, stack.available, stack.queued, tail,
+ "%s[ head=%2d, available=%2d, queued=%2d ] "
+ "reallocMask=%08x, inUse=%2d, identity=%d, status=%d",
+ prefix, stack.head, stack.available, stack.queued,
stack.reallocMask, stack.inUse, stack.identity, stack.status);
result.append(buffer);
+ result.append("\n");
return result;
}
+status_t SharedBufferBase::waitForCondition(const ConditionBase& condition)
+{
+ const SharedBufferStack& stack( *mSharedStack );
+ SharedClient& client( *mSharedClient );
+ const nsecs_t TIMEOUT = s2ns(1);
+ const int identity = mIdentity;
+
+ Mutex::Autolock _l(client.lock);
+ while ((condition()==false) &&
+ (stack.identity == identity) &&
+ (stack.status == NO_ERROR))
+ {
+ status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
+ // handle errors and timeouts
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ if (err == TIMED_OUT) {
+ if (condition()) {
+ LOGE("waitForCondition(%s) timed out (identity=%d), "
+ "but condition is true! We recovered but it "
+ "shouldn't happen." , condition.name(), stack.identity);
+ break;
+ } else {
+ LOGW("waitForCondition(%s) timed out "
+ "(identity=%d, status=%d). "
+ "CPU may be pegged. trying again.", condition.name(),
+ stack.identity, stack.status);
+ }
+ } else {
+ LOGE("waitForCondition(%s) error (%s) ",
+ condition.name(), strerror(-err));
+ return err;
+ }
+ }
+ }
+ return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;
+}
// ============================================================================
// conditions and updates
// ============================================================================
@@ -149,26 +250,21 @@ String8 SharedBufferBase::dump(char const* prefix) const
SharedBufferClient::DequeueCondition::DequeueCondition(
SharedBufferClient* sbc) : ConditionBase(sbc) {
}
-bool SharedBufferClient::DequeueCondition::operator()() {
+bool SharedBufferClient::DequeueCondition::operator()() const {
return stack.available > 0;
}
SharedBufferClient::LockCondition::LockCondition(
SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
}
-bool SharedBufferClient::LockCondition::operator()() {
- return (buf != stack.head ||
+bool SharedBufferClient::LockCondition::operator()() const {
+ // NOTE: if stack.head is messed up, we could crash the client
+ // or cause some drawing artifacts. This is okay, as long as it is
+ // limited to the client.
+ return (buf != stack.index[stack.head] ||
(stack.queued > 0 && stack.inUse != buf));
}
-SharedBufferServer::ReallocateCondition::ReallocateCondition(
- SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
-}
-bool SharedBufferServer::ReallocateCondition::operator()() {
- // TODO: we should also check that buf has been dequeued
- return (buf != stack.head);
-}
-
// ----------------------------------------------------------------------------
SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
@@ -179,10 +275,22 @@ ssize_t SharedBufferClient::QueueUpdate::operator()() {
return NO_ERROR;
}
-SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
- : UpdateBase(sbb) {
+SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
}
-ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ssize_t SharedBufferClient::DequeueUpdate::operator()() {
+ if (android_atomic_dec(&stack.available) == 0) {
+ LOGW("dequeue probably called from multiple threads!");
+ }
+ return NO_ERROR;
+}
+
+SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb,
+ int tail, int buf)
+ : UpdateBase(sbb), tail(tail), buf(buf) {
+}
+ssize_t SharedBufferClient::CancelUpdate::operator()() {
+ stack.index[tail] = buf;
android_atomic_inc(&stack.available);
return NO_ERROR;
}
@@ -193,8 +301,10 @@ SharedBufferServer::UnlockUpdate::UnlockUpdate(
}
ssize_t SharedBufferServer::UnlockUpdate::operator()() {
if (stack.inUse != lockedBuffer) {
- LOGE("unlocking %d, but currently locked buffer is %d",
- lockedBuffer, stack.inUse);
+ LOGE("unlocking %d, but currently locked buffer is %d "
+ "(identity=%d, token=%d)",
+ lockedBuffer, stack.inUse,
+ stack.identity, stack.token);
return BAD_VALUE;
}
android_atomic_write(-1, &stack.inUse);
@@ -206,11 +316,12 @@ SharedBufferServer::RetireUpdate::RetireUpdate(
: UpdateBase(sbb), numBuffers(numBuffers) {
}
ssize_t SharedBufferServer::RetireUpdate::operator()() {
- // head is only written in this function, which is single-thread.
int32_t head = stack.head;
+ if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX)
+ return BAD_VALUE;
// Preventively lock the current buffer before updating queued.
- android_atomic_write(head, &stack.inUse);
+ android_atomic_write(stack.headBuf, &stack.inUse);
// Decrement the number of queued buffers
int32_t queued;
@@ -221,16 +332,17 @@ ssize_t SharedBufferServer::RetireUpdate::operator()() {
}
} while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
- // update the head pointer
- head = ((head+1 >= numBuffers) ? 0 : head+1);
-
// lock the buffer before advancing head, which automatically unlocks
// the buffer we preventively locked upon entering this function
- android_atomic_write(head, &stack.inUse);
- // advance head
+ head = (head + 1) % numBuffers;
+ const int8_t headBuf = stack.index[head];
+ stack.headBuf = headBuf;
+ android_atomic_write(headBuf, &stack.inUse);
+
+ // head is only modified here, so we don't need to use cmpxchg
android_atomic_write(head, &stack.head);
-
+
// now that head has moved, we can increment the number of available buffers
android_atomic_inc(&stack.available);
return head;
@@ -250,41 +362,31 @@ ssize_t SharedBufferServer::StatusUpdate::operator()() {
SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
int surface, int num, int32_t identity)
- : SharedBufferBase(sharedClient, surface, num, identity), tail(0)
+ : SharedBufferBase(sharedClient, surface, identity),
+ mNumBuffers(num), tail(0)
{
+ SharedBufferStack& stack( *mSharedStack );
tail = computeTail();
+ queued_head = stack.head;
}
int32_t SharedBufferClient::computeTail() const
{
SharedBufferStack& stack( *mSharedStack );
- // we need to make sure we read available and head coherently,
- // w.r.t RetireUpdate.
- int32_t newTail;
- int32_t avail;
- int32_t head;
- do {
- avail = stack.available;
- head = stack.head;
- } while (stack.available != avail);
- newTail = head - avail + 1;
- if (newTail < 0) {
- newTail += mNumBuffers;
- } else if (newTail >= mNumBuffers) {
- newTail -= mNumBuffers;
- }
- return newTail;
+ return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers;
}
ssize_t SharedBufferClient::dequeue()
{
SharedBufferStack& stack( *mSharedStack );
- if (stack.head == tail && stack.available == 2) {
+ if (stack.head == tail && stack.available == mNumBuffers) {
LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
tail, stack.head, stack.available, stack.queued);
}
-
+
+ RWLock::AutoRLock _rd(mLock);
+
const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
//LOGD("[%d] about to dequeue a buffer",
@@ -294,16 +396,12 @@ ssize_t SharedBufferClient::dequeue()
if (err != NO_ERROR)
return ssize_t(err);
- // NOTE: 'stack.available' is part of the conditions, however
- // decrementing it, never changes any conditions, so we don't need
- // to do this as part of an update.
- if (android_atomic_dec(&stack.available) == 0) {
- LOGW("dequeue probably called from multiple threads!");
- }
+ DequeueUpdate update(this);
+ updateCondition( update );
- int dequeued = tail;
+ int dequeued = stack.index[tail];
tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
- LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s",
+ LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s",
dequeued, tail, dump("").string());
mDequeueTime[dequeued] = dequeueTime;
@@ -313,16 +411,28 @@ ssize_t SharedBufferClient::dequeue()
status_t SharedBufferClient::undoDequeue(int buf)
{
- UndoDequeueUpdate update(this);
+ return cancel(buf);
+}
+
+status_t SharedBufferClient::cancel(int buf)
+{
+ RWLock::AutoRLock _rd(mLock);
+
+ // calculate the new position of the tail index (essentially tail--)
+ int localTail = (tail + mNumBuffers - 1) % mNumBuffers;
+ CancelUpdate update(this, localTail, buf);
status_t err = updateCondition( update );
if (err == NO_ERROR) {
- tail = computeTail();
+ tail = localTail;
}
return err;
}
status_t SharedBufferClient::lock(int buf)
{
+ RWLock::AutoRLock _rd(mLock);
+
+ SharedBufferStack& stack( *mSharedStack );
LockCondition condition(this, buf);
status_t err = waitForCondition(condition);
return err;
@@ -330,53 +440,111 @@ status_t SharedBufferClient::lock(int buf)
status_t SharedBufferClient::queue(int buf)
{
+ RWLock::AutoRLock _rd(mLock);
+
+ SharedBufferStack& stack( *mSharedStack );
+
+ queued_head = (queued_head + 1) % mNumBuffers;
+ stack.index[queued_head] = buf;
+
QueueUpdate update(this);
status_t err = updateCondition( update );
LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string());
- SharedBufferStack& stack( *mSharedStack );
+
const nsecs_t now = systemTime(SYSTEM_TIME_THREAD);
stack.stats.totalTime = ns2us(now - mDequeueTime[buf]);
return err;
}
-bool SharedBufferClient::needNewBuffer(int buffer) const
+bool SharedBufferClient::needNewBuffer(int buf) const
{
SharedBufferStack& stack( *mSharedStack );
- const uint32_t mask = 1<<buffer;
+ const uint32_t mask = 1<<(31-buf);
return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;
}
-status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg)
+status_t SharedBufferClient::setCrop(int buf, const Rect& crop)
{
SharedBufferStack& stack( *mSharedStack );
- return stack.setDirtyRegion(buffer, reg);
+ return stack.setCrop(buf, crop);
+}
+
+status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.setTransform(buf, uint8_t(transform));
+}
+
+status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.setDirtyRegion(buf, reg);
+}
+
+status_t SharedBufferClient::setBufferCount(
+ int bufferCount, const SetBufferCountCallback& ipc)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX)
+ return BAD_VALUE;
+
+ if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN)
+ return BAD_VALUE;
+
+ RWLock::AutoWLock _wr(mLock);
+
+ status_t err = ipc(bufferCount);
+ if (err == NO_ERROR) {
+ mNumBuffers = bufferCount;
+ queued_head = (stack.head + stack.queued) % mNumBuffers;
+ }
+ return err;
}
// ----------------------------------------------------------------------------
SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
int surface, int num, int32_t identity)
- : SharedBufferBase(sharedClient, surface, num, identity)
+ : SharedBufferBase(sharedClient, surface, identity),
+ mNumBuffers(num)
{
mSharedStack->init(identity);
+ mSharedStack->token = surface;
mSharedStack->head = num-1;
mSharedStack->available = num;
mSharedStack->queued = 0;
mSharedStack->reallocMask = 0;
- memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion));
+ memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers));
+ for (int i=0 ; i<num ; i++) {
+ mBufferList.add(i);
+ mSharedStack->index[i] = i;
+ }
+}
+
+SharedBufferServer::~SharedBufferServer()
+{
}
ssize_t SharedBufferServer::retireAndLock()
{
+ RWLock::AutoRLock _l(mLock);
+
RetireUpdate update(this, mNumBuffers);
ssize_t buf = updateCondition( update );
- LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string());
+ if (buf >= 0) {
+ if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX)
+ return BAD_VALUE;
+ SharedBufferStack& stack( *mSharedStack );
+ buf = stack.index[buf];
+ LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s",
+ int(buf), dump("").string());
+ }
return buf;
}
-status_t SharedBufferServer::unlock(int buffer)
+status_t SharedBufferServer::unlock(int buf)
{
- UnlockUpdate update(this, buffer);
+ UnlockUpdate update(this, buf);
status_t err = updateCondition( update );
return err;
}
@@ -389,11 +557,25 @@ void SharedBufferServer::setStatus(status_t status)
}
}
-status_t SharedBufferServer::reallocate()
+status_t SharedBufferServer::reallocateAll()
{
+ RWLock::AutoRLock _l(mLock);
+
+ SharedBufferStack& stack( *mSharedStack );
+ uint32_t mask = mBufferList.getMask();
+ android_atomic_or(mask, &stack.reallocMask);
+ return NO_ERROR;
+}
+
+status_t SharedBufferServer::reallocateAllExcept(int buffer)
+{
+ RWLock::AutoRLock _l(mLock);
+
SharedBufferStack& stack( *mSharedStack );
- uint32_t mask = (1<<mNumBuffers)-1;
- android_atomic_or(mask, &stack.reallocMask);
+ BufferList temp(mBufferList);
+ temp.remove(buffer);
+ uint32_t mask = temp.getMask();
+ android_atomic_or(mask, &stack.reallocMask);
return NO_ERROR;
}
@@ -403,17 +585,74 @@ int32_t SharedBufferServer::getQueuedCount() const
return stack.queued;
}
-status_t SharedBufferServer::assertReallocate(int buffer)
+Region SharedBufferServer::getDirtyRegion(int buf) const
{
- ReallocateCondition condition(this, buffer);
- status_t err = waitForCondition(condition);
- return err;
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getDirtyRegion(buf);
+}
+
+Rect SharedBufferServer::getCrop(int buf) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getCrop(buf);
}
-Region SharedBufferServer::getDirtyRegion(int buffer) const
+uint32_t SharedBufferServer::getTransform(int buf) const
{
SharedBufferStack& stack( *mSharedStack );
- return stack.getDirtyRegion(buffer);
+ return stack.getTransform(buf);
+}
+
+/*
+ * NOTE: this is not thread-safe on the server-side, meaning
+ * 'head' cannot move during this operation. The client-side
+ * can safely operate an usual.
+ *
+ */
+status_t SharedBufferServer::resize(int newNumBuffers)
+{
+ if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX)
+ return BAD_VALUE;
+
+ RWLock::AutoWLock _l(mLock);
+
+ // for now we're not supporting shrinking
+ const int numBuffers = mNumBuffers;
+ if (newNumBuffers < numBuffers)
+ return BAD_VALUE;
+
+ SharedBufferStack& stack( *mSharedStack );
+ const int extra = newNumBuffers - numBuffers;
+
+ // read the head, make sure it's valid
+ int32_t head = stack.head;
+ if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX)
+ return BAD_VALUE;
+
+ int base = numBuffers;
+ int32_t avail = stack.available;
+ int tail = head - avail + 1;
+
+ if (tail >= 0) {
+ int8_t* const index = const_cast<int8_t*>(stack.index);
+ const int nb = numBuffers - head;
+ memmove(&index[head + extra], &index[head], nb);
+ base = head;
+ // move head 'extra' ahead, this doesn't impact stack.index[head];
+ stack.head = head + extra;
+ }
+ stack.available += extra;
+
+ // fill the new free space with unused buffers
+ BufferList::const_iterator curr(mBufferList.free_begin());
+ for (int i=0 ; i<extra ; i++) {
+ stack.index[base+i] = *curr;
+ mBufferList.add(*curr);
+ ++curr;
+ }
+
+ mNumBuffers = newNumBuffers;
+ return NO_ERROR;
}
SharedBufferStack::Statistics SharedBufferServer::getStats() const
@@ -422,6 +661,29 @@ SharedBufferStack::Statistics SharedBufferServer::getStats() const
return stack.stats;
}
+// ---------------------------------------------------------------------------
+status_t SharedBufferServer::BufferList::add(int value)
+{
+ if (uint32_t(value) >= mCapacity)
+ return BAD_VALUE;
+ uint32_t mask = 1<<(31-value);
+ if (mList & mask)
+ return ALREADY_EXISTS;
+ mList |= mask;
+ return NO_ERROR;
+}
+
+status_t SharedBufferServer::BufferList::remove(int value)
+{
+ if (uint32_t(value) >= mCapacity)
+ return BAD_VALUE;
+ uint32_t mask = 1<<(31-value);
+ if (!(mList & mask))
+ return NAME_NOT_FOUND;
+ mList &= ~mask;
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 5dd75c3..854a3c6 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -17,8 +17,6 @@
#define LOG_TAG "Surface"
#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -28,14 +26,13 @@
#include <utils/CallStack.h>
#include <utils/Log.h>
-#include <pixelflinger/pixelflinger.h>
-
#include <binder/IPCThreadState.h>
#include <binder/IMemory.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicBufferMapper.h>
+#include <ui/GraphicLog.h>
#include <ui/Rect.h>
#include <surfaceflinger/Surface.h>
@@ -55,6 +52,8 @@ static status_t copyBlt(
const sp<GraphicBuffer>& src,
const Region& reg)
{
+ // src and dst with, height and format must be identical. no verification
+ // is done here.
status_t err;
uint8_t const * src_bits = NULL;
err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
@@ -67,7 +66,6 @@ static status_t copyBlt(
Region::const_iterator head(reg.begin());
Region::const_iterator tail(reg.end());
if (head != tail && src_bits && dst_bits) {
- // NOTE: dst and src must be the same format
const size_t bpp = bytesPerPixel(src->format);
const size_t dbpr = dst->stride * bpp;
const size_t sbpr = src->stride * bpp;
@@ -107,7 +105,7 @@ static status_t copyBlt(
SurfaceControl::SurfaceControl(
const sp<SurfaceComposerClient>& client,
const sp<ISurface>& surface,
- const ISurfaceFlingerClient::surface_data_t& data,
+ const ISurfaceComposerClient::surface_data_t& data,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)
: mClient(client), mSurface(surface),
mToken(data.token), mIdentity(data.identity),
@@ -154,75 +152,75 @@ bool SurfaceControl::isSameSurface(
}
status_t SurfaceControl::setLayer(int32_t layer) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setLayer(mToken, layer);
}
status_t SurfaceControl::setPosition(int32_t x, int32_t y) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setPosition(mToken, x, y);
}
status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setSize(mToken, w, h);
}
status_t SurfaceControl::hide() {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->hide(mToken);
}
status_t SurfaceControl::show(int32_t layer) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->show(mToken, layer);
}
status_t SurfaceControl::freeze() {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->freeze(mToken);
}
status_t SurfaceControl::unfreeze() {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->unfreeze(mToken);
}
status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setFlags(mToken, flags, mask);
}
status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setTransparentRegionHint(mToken, transparent);
}
status_t SurfaceControl::setAlpha(float alpha) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setAlpha(mToken, alpha);
}
status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy);
}
status_t SurfaceControl::setFreezeTint(uint32_t tint) {
- const sp<SurfaceComposerClient>& client(mClient);
status_t err = validate();
if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
return client->setFreezeTint(mToken, tint);
}
@@ -233,50 +231,27 @@ status_t SurfaceControl::validate() const
mToken, mIdentity, mClient.get());
return NO_INIT;
}
- SharedClient const* cblk = mClient->mControl;
- if (cblk == 0) {
- LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
- return NO_INIT;
- }
- status_t err = cblk->validate(mToken);
- if (err != NO_ERROR) {
- LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
- mToken, mIdentity, err, strerror(-err));
- return err;
- }
- uint32_t identity = cblk->getIdentity(mToken);
- if (mIdentity != identity) {
- LOGE("using an invalid surface id=%d, identity=%u should be %d",
- mToken, mIdentity, identity);
- return NO_INIT;
- }
return NO_ERROR;
}
status_t SurfaceControl::writeSurfaceToParcel(
const sp<SurfaceControl>& control, Parcel* parcel)
{
- uint32_t flags = 0;
- uint32_t format = 0;
- SurfaceID token = -1;
+ sp<ISurface> sur;
uint32_t identity = 0;
uint32_t width = 0;
uint32_t height = 0;
- sp<SurfaceComposerClient> client;
- sp<ISurface> sur;
+ uint32_t format = 0;
+ uint32_t flags = 0;
if (SurfaceControl::isValid(control)) {
- token = control->mToken;
- identity = control->mIdentity;
- client = control->mClient;
sur = control->mSurface;
+ identity = control->mIdentity;
width = control->mWidth;
height = control->mHeight;
format = control->mFormat;
flags = control->mFlags;
}
- parcel->writeStrongBinder(client!=0 ? client->connection() : NULL);
- parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
- parcel->writeInt32(token);
+ parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
parcel->writeInt32(identity);
parcel->writeInt32(width);
parcel->writeInt32(height);
@@ -298,70 +273,181 @@ sp<Surface> SurfaceControl::getSurface() const
// Surface
// ============================================================================
+class SurfaceClient : public Singleton<SurfaceClient>
+{
+ // all these attributes are constants
+ sp<ISurfaceComposer> mComposerService;
+ sp<ISurfaceComposerClient> mClient;
+ status_t mStatus;
+ SharedClient* mControl;
+ sp<IMemoryHeap> mControlMemory;
+
+ SurfaceClient()
+ : Singleton<SurfaceClient>(), mStatus(NO_INIT)
+ {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ mComposerService = sf;
+ mClient = sf->createClientConnection();
+ if (mClient != NULL) {
+ mControlMemory = mClient->getControlBlock();
+ if (mControlMemory != NULL) {
+ mControl = static_cast<SharedClient *>(
+ mControlMemory->getBase());
+ if (mControl) {
+ mStatus = NO_ERROR;
+ }
+ }
+ }
+ }
+ friend class Singleton<SurfaceClient>;
+public:
+ status_t initCheck() const {
+ return mStatus;
+ }
+ SharedClient* getSharedClient() const {
+ return mControl;
+ }
+ ssize_t getTokenForSurface(const sp<ISurface>& sur) const {
+ // TODO: we could cache a few tokens here to avoid an IPC
+ return mClient->getTokenForSurface(sur);
+ }
+ void signalServer() const {
+ mComposerService->signal();
+ }
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient);
+
+// ---------------------------------------------------------------------------
+
Surface::Surface(const sp<SurfaceControl>& surface)
- : mClient(surface->mClient), mSurface(surface->mSurface),
- mToken(surface->mToken), mIdentity(surface->mIdentity),
+ : mBufferMapper(GraphicBufferMapper::get()),
+ mClient(SurfaceClient::getInstance()),
+ mSharedBufferClient(NULL),
+ mInitCheck(NO_INIT),
+ mSurface(surface->mSurface),
+ mIdentity(surface->mIdentity),
mFormat(surface->mFormat), mFlags(surface->mFlags),
- mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL),
mWidth(surface->mWidth), mHeight(surface->mHeight)
{
- mSharedBufferClient = new SharedBufferClient(
- mClient->mControl, mToken, 2, mIdentity);
-
init();
}
-Surface::Surface(const Parcel& parcel)
- : mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL)
+Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
+ : mBufferMapper(GraphicBufferMapper::get()),
+ mClient(SurfaceClient::getInstance()),
+ mSharedBufferClient(NULL),
+ mInitCheck(NO_INIT)
{
- sp<IBinder> clientBinder = parcel.readStrongBinder();
- mSurface = interface_cast<ISurface>(parcel.readStrongBinder());
- mToken = parcel.readInt32();
+ mSurface = interface_cast<ISurface>(ref);
mIdentity = parcel.readInt32();
mWidth = parcel.readInt32();
mHeight = parcel.readInt32();
mFormat = parcel.readInt32();
mFlags = parcel.readInt32();
+ init();
+}
+
+status_t Surface::writeToParcel(
+ const sp<Surface>& surface, Parcel* parcel)
+{
+ sp<ISurface> sur;
+ uint32_t identity = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ uint32_t format = 0;
+ uint32_t flags = 0;
+ if (Surface::isValid(surface)) {
+ sur = surface->mSurface;
+ identity = surface->mIdentity;
+ width = surface->mWidth;
+ height = surface->mHeight;
+ format = surface->mFormat;
+ flags = surface->mFlags;
+ }
+ parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+ parcel->writeInt32(identity);
+ parcel->writeInt32(width);
+ parcel->writeInt32(height);
+ parcel->writeInt32(format);
+ parcel->writeInt32(flags);
+ return NO_ERROR;
- // FIXME: what does that mean if clientBinder is NULL here?
- if (clientBinder != NULL) {
- mClient = SurfaceComposerClient::clientForConnection(clientBinder);
+}
- mSharedBufferClient = new SharedBufferClient(
- mClient->mControl, mToken, 2, mIdentity);
+
+Mutex Surface::sCachedSurfacesLock;
+DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces(wp<Surface>(0));
+
+sp<Surface> Surface::readFromParcel(const Parcel& data) {
+ Mutex::Autolock _l(sCachedSurfacesLock);
+ sp<IBinder> binder(data.readStrongBinder());
+ sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
+ if (surface == 0) {
+ surface = new Surface(data, binder);
+ sCachedSurfaces.add(binder, surface);
}
+ if (surface->mSurface == 0) {
+ surface = 0;
+ }
+ cleanCachedSurfaces();
+ return surface;
+}
- init();
+// Remove the stale entries from the surface cache. This should only be called
+// with sCachedSurfacesLock held.
+void Surface::cleanCachedSurfaces() {
+ for (int i = sCachedSurfaces.size()-1; i >= 0; --i) {
+ wp<Surface> s(sCachedSurfaces.valueAt(i));
+ if (s == 0 || s.promote() == 0) {
+ sCachedSurfaces.removeItemsAt(i);
+ }
+ }
}
void Surface::init()
{
- android_native_window_t::setSwapInterval = setSwapInterval;
- android_native_window_t::dequeueBuffer = dequeueBuffer;
- android_native_window_t::lockBuffer = lockBuffer;
- android_native_window_t::queueBuffer = queueBuffer;
- android_native_window_t::query = query;
- android_native_window_t::perform = perform;
- mSwapRectangle.makeInvalid();
+ ANativeWindow::setSwapInterval = setSwapInterval;
+ ANativeWindow::dequeueBuffer = dequeueBuffer;
+ ANativeWindow::cancelBuffer = cancelBuffer;
+ ANativeWindow::lockBuffer = lockBuffer;
+ ANativeWindow::queueBuffer = queueBuffer;
+ ANativeWindow::query = query;
+ ANativeWindow::perform = perform;
+
DisplayInfo dinfo;
SurfaceComposerClient::getDisplayInfo(0, &dinfo);
- const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi;
- const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi;
+ const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi;
+ const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi;
// FIXME: set real values here
- const_cast<int&>(android_native_window_t::minSwapInterval) = 1;
- const_cast<int&>(android_native_window_t::maxSwapInterval) = 1;
- const_cast<uint32_t&>(android_native_window_t::flags) = 0;
- // be default we request a hardware surface
- mUsage = GRALLOC_USAGE_HW_RENDER;
+ const_cast<int&>(ANativeWindow::minSwapInterval) = 1;
+ const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
+ const_cast<uint32_t&>(ANativeWindow::flags) = 0;
+
+ mNextBufferTransform = 0;
mConnected = 0;
- mNeedFullUpdate = false;
+ mSwapRectangle.makeInvalid();
+ mNextBufferCrop = Rect(0,0);
+ // two buffers by default
+ mBuffers.setCapacity(2);
+ mBuffers.insertAt(0, 2);
+
+ if (mSurface != 0 && mClient.initCheck() == NO_ERROR) {
+ int32_t token = mClient.getTokenForSurface(mSurface);
+ if (token >= 0) {
+ mSharedBufferClient = new SharedBufferClient(
+ mClient.getSharedClient(), token, 2, mIdentity);
+ mInitCheck = mClient.getSharedClient()->validate(token);
+ }
+ }
}
Surface::~Surface()
{
// this is a client-side operation, the surface is destroyed, unmap
// its buffers in this process.
- for (int i=0 ; i<2 ; i++) {
+ size_t size = mBuffers.size();
+ for (size_t i=0 ; i<size ; i++) {
if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) {
getBufferMapper().unregisterBuffer(mBuffers[i]->handle);
}
@@ -369,93 +455,94 @@ Surface::~Surface()
// clear all references and trigger an IPC now, to make sure things
// happen without delay, since these resources are quite heavy.
- mClient.clear();
+ mBuffers.clear();
mSurface.clear();
delete mSharedBufferClient;
IPCThreadState::self()->flushCommands();
}
-sp<SurfaceComposerClient> Surface::getClient() const {
- return mClient;
-}
-
-sp<ISurface> Surface::getISurface() const {
- return mSurface;
-}
-
bool Surface::isValid() {
- return mToken>=0 && mClient!=0;
+ return mInitCheck == NO_ERROR;
}
status_t Surface::validate() const
{
- sp<SurfaceComposerClient> client(getClient());
- if (mToken<0 || mClient==0) {
- LOGE("invalid token (%d, identity=%u) or client (%p)",
- mToken, mIdentity, client.get());
- return NO_INIT;
+ // check that we initialized ourself properly
+ if (mInitCheck != NO_ERROR) {
+ LOGE("invalid token (identity=%u)", mIdentity);
+ return mInitCheck;
}
- SharedClient const* cblk = mClient->mControl;
- if (cblk == 0) {
- LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
+
+ // verify the identity of this surface
+ uint32_t identity = mSharedBufferClient->getIdentity();
+
+ // this is a bit of a (temporary) special case, identity==0 means that
+ // no operation are allowed from the client (eg: dequeue/queue), this
+ // is used with PUSH_BUFFER surfaces for instance
+ if (identity == 0) {
+ LOGE("[Surface] invalid operation (identity=%u)", mIdentity);
+ return INVALID_OPERATION;
+ }
+
+ if (mIdentity != identity) {
+ LOGE("[Surface] using an invalid surface, "
+ "identity=%u should be %d",
+ mIdentity, identity);
return NO_INIT;
}
- status_t err = cblk->validate(mToken);
+
+ // check the surface didn't become invalid
+ status_t err = mSharedBufferClient->getStatus();
if (err != NO_ERROR) {
- LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
- mToken, mIdentity, err, strerror(-err));
+ LOGE("surface (identity=%u) is invalid, err=%d (%s)",
+ mIdentity, err, strerror(-err));
return err;
}
- uint32_t identity = cblk->getIdentity(mToken);
- if (mIdentity != identity) {
- LOGE("using an invalid surface id=%d, identity=%u should be %d",
- mToken, mIdentity, identity);
- return NO_INIT;
- }
+
return NO_ERROR;
}
-
-bool Surface::isSameSurface(
- const sp<Surface>& lhs, const sp<Surface>& rhs)
-{
- if (lhs == 0 || rhs == 0)
- return false;
-
- return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
+sp<ISurface> Surface::getISurface() const {
+ return mSurface;
}
// ----------------------------------------------------------------------------
-int Surface::setSwapInterval(android_native_window_t* window, int interval) {
+int Surface::setSwapInterval(ANativeWindow* window, int interval) {
return 0;
}
-int Surface::dequeueBuffer(android_native_window_t* window,
+int Surface::dequeueBuffer(ANativeWindow* window,
android_native_buffer_t** buffer) {
Surface* self = getSelf(window);
return self->dequeueBuffer(buffer);
}
-int Surface::lockBuffer(android_native_window_t* window,
+int Surface::cancelBuffer(ANativeWindow* window,
+ android_native_buffer_t* buffer) {
+ Surface* self = getSelf(window);
+ return self->cancelBuffer(buffer);
+}
+
+int Surface::lockBuffer(ANativeWindow* window,
android_native_buffer_t* buffer) {
Surface* self = getSelf(window);
return self->lockBuffer(buffer);
}
-int Surface::queueBuffer(android_native_window_t* window,
+int Surface::queueBuffer(ANativeWindow* window,
android_native_buffer_t* buffer) {
Surface* self = getSelf(window);
return self->queueBuffer(buffer);
}
-int Surface::query(android_native_window_t* window,
+int Surface::query(ANativeWindow* window,
int what, int* value) {
Surface* self = getSelf(window);
return self->query(what, value);
}
-int Surface::perform(android_native_window_t* window,
+int Surface::perform(ANativeWindow* window,
int operation, ...) {
va_list args;
va_start(args, operation);
@@ -467,49 +554,62 @@ int Surface::perform(android_native_window_t* window,
// ----------------------------------------------------------------------------
-status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer) {
- android_native_buffer_t* out;
- status_t err = dequeueBuffer(&out);
- if (err == NO_ERROR) {
- *buffer = GraphicBuffer::getSelf(out);
+bool Surface::needNewBuffer(int bufIdx,
+ uint32_t *pWidth, uint32_t *pHeight,
+ uint32_t *pFormat, uint32_t *pUsage) const
+{
+ Mutex::Autolock _l(mSurfaceLock);
+
+ // Always call needNewBuffer(), since it clears the needed buffers flags
+ bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx);
+ bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]);
+ bool newNeewBuffer = needNewBuffer || !validBuffer;
+ if (newNeewBuffer) {
+ mBufferInfo.get(pWidth, pHeight, pFormat, pUsage);
}
- return err;
+ return newNeewBuffer;
}
-// ----------------------------------------------------------------------------
-
-
int Surface::dequeueBuffer(android_native_buffer_t** buffer)
{
- sp<SurfaceComposerClient> client(getClient());
status_t err = validate();
if (err != NO_ERROR)
return err;
+ GraphicLog& logger(GraphicLog::getInstance());
+ logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1);
+
ssize_t bufIdx = mSharedBufferClient->dequeue();
+
+ logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx);
+
if (bufIdx < 0) {
LOGE("error dequeuing a buffer (%s)", strerror(bufIdx));
return bufIdx;
}
- // below we make sure we AT LEAST have the usage flags we want
- const uint32_t usage(getUsage());
- const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
- if (backBuffer == 0 ||
- ((uint32_t(backBuffer->usage) & usage) != usage) ||
- mSharedBufferClient->needNewBuffer(bufIdx))
- {
- err = getBufferLocked(bufIdx, usage);
- LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)",
- bufIdx, usage, strerror(-err));
+ // grow the buffer array if needed
+ const size_t size = mBuffers.size();
+ const size_t needed = bufIdx+1;
+ if (size < needed) {
+ mBuffers.insertAt(size, needed-size);
+ }
+
+ uint32_t w, h, format, usage;
+ if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) {
+ err = getBufferLocked(bufIdx, w, h, format, usage);
+ LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)",
+ bufIdx, w, h, format, usage, strerror(-err));
if (err == NO_ERROR) {
// reset the width/height with the what we get from the buffer
+ const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
mWidth = uint32_t(backBuffer->width);
mHeight = uint32_t(backBuffer->height);
}
}
// if we still don't have a buffer here, we probably ran out of memory
+ const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]);
if (!err && backBuffer==0) {
err = NO_MEMORY;
}
@@ -524,22 +624,54 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer)
return err;
}
+int Surface::cancelBuffer(android_native_buffer_t* buffer)
+{
+ status_t err = validate();
+ switch (err) {
+ case NO_ERROR:
+ // no error, common case
+ break;
+ case INVALID_OPERATION:
+ // legitimate errors here
+ return err;
+ default:
+ // other errors happen because the surface is now invalid,
+ // for instance because it has been destroyed. In this case,
+ // we just fail silently (canceling a buffer is not technically
+ // an error at this point)
+ return NO_ERROR;
+ }
+
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+ err = mSharedBufferClient->cancel(bufIdx);
+
+ LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err));
+ return err;
+}
+
+
int Surface::lockBuffer(android_native_buffer_t* buffer)
{
- sp<SurfaceComposerClient> client(getClient());
status_t err = validate();
if (err != NO_ERROR)
return err;
- int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex();
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+ GraphicLog& logger(GraphicLog::getInstance());
+ logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx);
+
err = mSharedBufferClient->lock(bufIdx);
+
+ logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx);
+
LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
return err;
}
int Surface::queueBuffer(android_native_buffer_t* buffer)
-{
- sp<SurfaceComposerClient> client(getClient());
+{
status_t err = validate();
if (err != NO_ERROR)
return err;
@@ -548,14 +680,19 @@ int Surface::queueBuffer(android_native_buffer_t* buffer)
mDirtyRegion.set(mSwapRectangle);
}
- int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex();
+ int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+
+ GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx);
+
+ mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
+ mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
err = mSharedBufferClient->queue(bufIdx);
LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
if (err == NO_ERROR) {
- // FIXME: can we avoid this IPC if we know there is one pending?
- client->signalServer();
+ // TODO: can we avoid this IPC if we know there is one pending?
+ mClient.signalServer();
}
return err;
}
@@ -578,6 +715,10 @@ int Surface::query(int what, int* value)
int Surface::perform(int operation, va_list args)
{
+ status_t err = validate();
+ if (err != NO_ERROR)
+ return err;
+
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_SET_USAGE:
@@ -589,6 +730,18 @@ int Surface::perform(int operation, va_list args)
case NATIVE_WINDOW_DISCONNECT:
res = dispatch_disconnect( args );
break;
+ case NATIVE_WINDOW_SET_CROP:
+ res = dispatch_crop( args );
+ break;
+ case NATIVE_WINDOW_SET_BUFFER_COUNT:
+ res = dispatch_set_buffer_count( args );
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
+ res = dispatch_set_buffers_geometry( args );
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+ res = dispatch_set_buffers_transform( args );
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -608,12 +761,30 @@ int Surface::dispatch_disconnect(va_list args) {
int api = va_arg(args, int);
return disconnect( api );
}
+int Surface::dispatch_crop(va_list args) {
+ android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
+ return crop( reinterpret_cast<Rect const*>(rect) );
+}
+int Surface::dispatch_set_buffer_count(va_list args) {
+ size_t bufferCount = va_arg(args, size_t);
+ return setBufferCount(bufferCount);
+}
+int Surface::dispatch_set_buffers_geometry(va_list args) {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ int f = va_arg(args, int);
+ return setBuffersGeometry(w, h, f);
+}
+int Surface::dispatch_set_buffers_transform(va_list args) {
+ int transform = va_arg(args, int);
+ return setBuffersTransform(transform);
+}
void Surface::setUsage(uint32_t reqUsage)
{
Mutex::Autolock _l(mSurfaceLock);
- mUsage = reqUsage;
+ mBufferInfo.set(reqUsage);
}
int Surface::connect(int api)
@@ -654,19 +825,77 @@ int Surface::disconnect(int api)
return err;
}
-uint32_t Surface::getUsage() const
+int Surface::crop(Rect const* rect)
{
+ // empty/invalid rects are not allowed
+ if (rect->isEmpty())
+ return BAD_VALUE;
+
Mutex::Autolock _l(mSurfaceLock);
- return mUsage;
+ // TODO: validate rect size
+ mNextBufferCrop = *rect;
+ return NO_ERROR;
}
+int Surface::setBufferCount(int bufferCount)
+{
+ sp<ISurface> s(mSurface);
+ if (s == 0) return NO_INIT;
+
+ class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback {
+ sp<ISurface> surface;
+ virtual status_t operator()(int bufferCount) const {
+ return surface->setBufferCount(bufferCount);
+ }
+ public:
+ SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { }
+ } ipc(s);
+
+ status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc);
+ LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s",
+ bufferCount, strerror(-err));
+ return err;
+}
+
+int Surface::setBuffersGeometry(int w, int h, int format)
+{
+ if (w<0 || h<0 || format<0)
+ return BAD_VALUE;
+
+ if ((w && !h) || (!w && h))
+ return BAD_VALUE;
+
+ Mutex::Autolock _l(mSurfaceLock);
+ if (mConnected == NATIVE_WINDOW_API_EGL) {
+ return INVALID_OPERATION;
+ }
+
+ mBufferInfo.set(w, h, format);
+ if (format != 0) {
+ // we update the format of the surface as reported by query().
+ // this is to allow applications to change the format of a surface's
+ // buffer, and have it reflected in EGL; which is needed for
+ // EGLConfig validation.
+ mFormat = format;
+ }
+ return NO_ERROR;
+}
+
+int Surface::setBuffersTransform(int transform)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ mNextBufferTransform = transform;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
int Surface::getConnectedApi() const
{
Mutex::Autolock _l(mSurfaceLock);
return mConnected;
}
-
// ----------------------------------------------------------------------------
status_t Surface::lock(SurfaceInfo* info, bool blocking) {
@@ -677,7 +906,7 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{
if (getConnectedApi()) {
LOGE("Surface::lock(%p) failed. Already connected to another API",
- (android_native_window_t*)this);
+ (ANativeWindow*)this);
CallStack stack;
stack.update();
stack.dump("");
@@ -703,45 +932,47 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
- sp<GraphicBuffer> backBuffer;
- status_t err = dequeueBuffer(&backBuffer);
+ android_native_buffer_t* out;
+ status_t err = dequeueBuffer(&out);
LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR) {
+ sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
err = lockBuffer(backBuffer.get());
LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)",
- backBuffer->getIndex(), strerror(-err));
+ getBufferIndex(backBuffer), strerror(-err));
if (err == NO_ERROR) {
- // we handle copy-back here...
-
const Rect bounds(backBuffer->width, backBuffer->height);
- Region scratch(bounds);
+ const Region boundsRegion(bounds);
+ Region scratch(boundsRegion);
Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);
+ newDirtyRegion &= boundsRegion;
- if (mNeedFullUpdate) {
- // reset newDirtyRegion to bounds when a buffer is reallocated
- // it would be better if this information was associated with
- // the buffer and made available to outside of Surface.
- // This will do for now though.
- mNeedFullUpdate = false;
- newDirtyRegion.set(bounds);
- } else {
- newDirtyRegion.andSelf(bounds);
- }
-
+ // figure out if we can copy the frontbuffer back
const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);
- if (frontBuffer !=0 &&
- backBuffer->width == frontBuffer->width &&
- backBuffer->height == frontBuffer->height &&
- !(mFlags & ISurfaceComposer::eDestroyBackbuffer))
- {
+ const bool canCopyBack = (frontBuffer != 0 &&
+ backBuffer->width == frontBuffer->width &&
+ backBuffer->height == frontBuffer->height &&
+ backBuffer->format == frontBuffer->format &&
+ !(mFlags & ISurfaceComposer::eDestroyBackbuffer));
+
+ // the dirty region we report to surfaceflinger is the one
+ // given by the user (as opposed to the one *we* return to the
+ // user).
+ mDirtyRegion = newDirtyRegion;
+
+ if (canCopyBack) {
+ // copy the area that is invalid and not repainted this round
const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
- if (!copyback.isEmpty() && frontBuffer!=0) {
- // copy front to back
+ if (!copyback.isEmpty())
copyBlt(backBuffer, frontBuffer, copyback);
- }
+ } else {
+ // if we can't copy-back anything, modify the user's dirty
+ // region to make sure they redraw the whole buffer
+ newDirtyRegion = boundsRegion;
}
- mDirtyRegion = newDirtyRegion;
+ // keep track of the are of the buffer that is "clean"
+ // (ie: that will be redrawn)
mOldDirtyRegion = newDirtyRegion;
void* vaddr;
@@ -777,7 +1008,7 @@ status_t Surface::unlockAndPost()
err = queueBuffer(mLockedBuffer.get());
LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)",
- mLockedBuffer->getIndex(), strerror(-err));
+ getBufferIndex(mLockedBuffer), strerror(-err));
mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
@@ -789,7 +1020,13 @@ void Surface::setSwapRectangle(const Rect& r) {
mSwapRectangle = r;
}
-status_t Surface::getBufferLocked(int index, int usage)
+int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const
+{
+ return buffer->getIndex();
+}
+
+status_t Surface::getBufferLocked(int index,
+ uint32_t w, uint32_t h, uint32_t format, uint32_t usage)
{
sp<ISurface> s(mSurface);
if (s == 0) return NO_INIT;
@@ -797,20 +1034,21 @@ status_t Surface::getBufferLocked(int index, int usage)
status_t err = NO_MEMORY;
// free the current buffer
- sp<GraphicBuffer>& currentBuffer(mBuffers[index]);
+ sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index));
if (currentBuffer != 0) {
getBufferMapper().unregisterBuffer(currentBuffer->handle);
currentBuffer.clear();
}
- sp<GraphicBuffer> buffer = s->requestBuffer(index, usage);
+ sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage);
LOGE_IF(buffer==0,
"ISurface::getBuffer(%d, %08x) returned NULL",
index, usage);
if (buffer != 0) { // this should never happen by construction
LOGE_IF(buffer->handle == NULL,
- "Surface (identity=%d) requestBuffer(%d, %08x) returned"
- "a buffer with a null handle", mIdentity, index, usage);
+ "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) "
+ "returned a buffer with a null handle",
+ mIdentity, index, w, h, format, usage);
err = mSharedBufferClient->getStatus();
LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err);
if (!err && buffer->handle != NULL) {
@@ -820,14 +1058,51 @@ status_t Surface::getBufferLocked(int index, int usage)
if (err == NO_ERROR) {
currentBuffer = buffer;
currentBuffer->setIndex(index);
- mNeedFullUpdate = true;
}
} else {
- err = err<0 ? err : NO_MEMORY;
+ err = err<0 ? err : status_t(NO_MEMORY);
}
}
return err;
}
-}; // namespace android
+// ----------------------------------------------------------------------------
+Surface::BufferInfo::BufferInfo()
+ : mWidth(0), mHeight(0), mFormat(0),
+ mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0)
+{
+}
+
+void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) {
+ if ((mWidth != w) || (mHeight != h) || (mFormat != format)) {
+ mWidth = w;
+ mHeight = h;
+ mFormat = format;
+ mDirty |= GEOMETRY;
+ }
+}
+
+void Surface::BufferInfo::set(uint32_t usage) {
+ mUsage = usage;
+}
+
+void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight,
+ uint32_t *pFormat, uint32_t *pUsage) const {
+ *pWidth = mWidth;
+ *pHeight = mHeight;
+ *pFormat = mFormat;
+ *pUsage = mUsage;
+}
+bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const {
+ // make sure we AT LEAST have the usage flags we want
+ if (mDirty || buffer==0 ||
+ ((buffer->usage & mUsage) != mUsage)) {
+ mDirty = 0;
+ return false;
+ }
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
index 3117495..f270461 100644
--- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp
+++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp
@@ -17,98 +17,137 @@
#define LOG_TAG "SurfaceComposerClient"
#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
#include <sys/types.h>
-#include <sys/stat.h>
-#include <cutils/memory.h>
-
-#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
-#include <utils/KeyedVector.h>
+#include <utils/SortedVector.h>
#include <utils/Log.h>
+#include <utils/Singleton.h>
#include <binder/IServiceManager.h>
#include <binder/IMemory.h>
#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/ISurfaceFlingerClient.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
#include <surfaceflinger/ISurface.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <private/surfaceflinger/LayerState.h>
#include <private/surfaceflinger/SharedBufferStack.h>
-#define VERBOSE(...) ((void)0)
-//#define VERBOSE LOGD
-
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+
+ComposerService::ComposerService()
+: Singleton<ComposerService>() {
+ const String16 name("SurfaceFlinger");
+ while (getService(name, &mComposerService) != NO_ERROR) {
+ usleep(250000);
+ }
+ mServerCblkMemory = mComposerService->getCblk();
+ mServerCblk = static_cast<surface_flinger_cblk_t volatile *>(
+ mServerCblkMemory->getBase());
+}
+
+sp<ISurfaceComposer> ComposerService::getComposerService() {
+ return ComposerService::getInstance().mComposerService;
+}
+
+surface_flinger_cblk_t const volatile * ComposerService::getControlBlock() {
+ return ComposerService::getInstance().mServerCblk;
+}
+
+static inline sp<ISurfaceComposer> getComposerService() {
+ return ComposerService::getComposerService();
+}
+
+static inline surface_flinger_cblk_t const volatile * get_cblk() {
+ return ComposerService::getControlBlock();
+}
// ---------------------------------------------------------------------------
-// Must not be holding SurfaceComposerClient::mLock when acquiring gLock here.
-static Mutex gLock;
-static sp<ISurfaceComposer> gSurfaceManager;
-static DefaultKeyedVector< sp<IBinder>, sp<SurfaceComposerClient> > gActiveConnections;
-static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions;
-static sp<IMemoryHeap> gServerCblkMemory;
-static volatile surface_flinger_cblk_t* gServerCblk;
-
-static sp<ISurfaceComposer> getComposerService()
-{
- sp<ISurfaceComposer> sc;
- Mutex::Autolock _l(gLock);
- if (gSurfaceManager != 0) {
- sc = gSurfaceManager;
- } else {
- // release the lock while we're waiting...
- gLock.unlock();
-
- sp<IBinder> binder;
- sp<IServiceManager> sm = defaultServiceManager();
- do {
- binder = sm->getService(String16("SurfaceFlinger"));
- if (binder == 0) {
- LOGW("SurfaceFlinger not published, waiting...");
- usleep(500000); // 0.5 s
+class Composer : public Singleton<Composer>
+{
+ Mutex mLock;
+ SortedVector< wp<SurfaceComposerClient> > mActiveConnections;
+ SortedVector<sp<SurfaceComposerClient> > mOpenTransactions;
+
+ Composer() : Singleton<Composer>() {
+ }
+
+ void addClientImpl(const sp<SurfaceComposerClient>& client) {
+ Mutex::Autolock _l(mLock);
+ mActiveConnections.add(client);
+ }
+
+ void removeClientImpl(const sp<SurfaceComposerClient>& client) {
+ Mutex::Autolock _l(mLock);
+ mActiveConnections.remove(client);
+ }
+
+ void openGlobalTransactionImpl()
+ {
+ Mutex::Autolock _l(mLock);
+ if (mOpenTransactions.size()) {
+ LOGE("openGlobalTransaction() called more than once. skipping.");
+ return;
+ }
+ const size_t N = mActiveConnections.size();
+ for (size_t i=0; i<N; i++) {
+ sp<SurfaceComposerClient> client(mActiveConnections[i].promote());
+ if (client != 0 && mOpenTransactions.indexOf(client) < 0) {
+ if (client->openTransaction() == NO_ERROR) {
+ mOpenTransactions.add(client);
+ } else {
+ LOGE("openTransaction on client %p failed", client.get());
+ // let it go, it'll fail later when the user
+ // tries to do something with the transaction
+ }
}
- } while(binder == 0);
-
- // grab the lock again for updating gSurfaceManager
- gLock.lock();
- if (gSurfaceManager == 0) {
- sc = interface_cast<ISurfaceComposer>(binder);
- gSurfaceManager = sc;
- } else {
- sc = gSurfaceManager;
}
}
- return sc;
-}
-static volatile surface_flinger_cblk_t const * get_cblk()
-{
- if (gServerCblk == 0) {
+ void closeGlobalTransactionImpl()
+ {
+ mLock.lock();
+ SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions);
+ mOpenTransactions.clear();
+ mLock.unlock();
+
sp<ISurfaceComposer> sm(getComposerService());
- Mutex::Autolock _l(gLock);
- if (gServerCblk == 0) {
- gServerCblkMemory = sm->getCblk();
- LOGE_IF(gServerCblkMemory==0, "Can't get server control block");
- gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase();
- LOGE_IF(gServerCblk==0, "Can't get server control block address");
- }
+ sm->openGlobalTransaction();
+ const size_t N = clients.size();
+ for (size_t i=0; i<N; i++) {
+ clients[i]->closeTransaction();
+ }
+ sm->closeGlobalTransaction();
}
- return gServerCblk;
-}
+
+ friend class Singleton<Composer>;
+
+public:
+ static void addClient(const sp<SurfaceComposerClient>& client) {
+ Composer::getInstance().addClientImpl(client);
+ }
+ static void removeClient(const sp<SurfaceComposerClient>& client) {
+ Composer::getInstance().removeClientImpl(client);
+ }
+ static void openGlobalTransaction() {
+ Composer::getInstance().openGlobalTransactionImpl();
+ }
+ static void closeGlobalTransaction() {
+ Composer::getInstance().closeGlobalTransactionImpl();
+ }
+};
+
+ANDROID_SINGLETON_STATIC_INSTANCE(Composer);
// ---------------------------------------------------------------------------
@@ -120,61 +159,27 @@ static inline int compare_type( const layer_state_t& lhs,
}
SurfaceComposerClient::SurfaceComposerClient()
+ : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT)
{
- sp<ISurfaceComposer> sm(getComposerService());
- if (sm == 0) {
- _init(0, 0);
- return;
- }
-
- _init(sm, sm->createConnection());
-
- if (mClient != 0) {
- Mutex::Autolock _l(gLock);
- VERBOSE("Adding client %p to map", this);
- gActiveConnections.add(mClient->asBinder(), this);
- }
-}
-
-SurfaceComposerClient::SurfaceComposerClient(
- const sp<ISurfaceComposer>& sm, const sp<IBinder>& conn)
-{
- _init(sm, interface_cast<ISurfaceFlingerClient>(conn));
}
-
-status_t SurfaceComposerClient::linkToComposerDeath(
- const sp<IBinder::DeathRecipient>& recipient,
- void* cookie, uint32_t flags)
+void SurfaceComposerClient::onFirstRef()
{
sp<ISurfaceComposer> sm(getComposerService());
- return sm->asBinder()->linkToDeath(recipient, cookie, flags);
-}
-
-void SurfaceComposerClient::_init(
- const sp<ISurfaceComposer>& sm, const sp<ISurfaceFlingerClient>& conn)
-{
- VERBOSE("Creating client %p, conn %p", this, conn.get());
-
- mPrebuiltLayerState = 0;
- mTransactionOpen = 0;
- mStatus = NO_ERROR;
- mControl = 0;
-
- mClient = conn;
- if (mClient == 0) {
- mStatus = NO_INIT;
- return;
+ if (sm != 0) {
+ sp<ISurfaceComposerClient> conn = sm->createConnection();
+ if (conn != 0) {
+ mClient = conn;
+ Composer::addClient(this);
+ mPrebuiltLayerState = new layer_state_t;
+ mStatus = NO_ERROR;
+ }
}
-
- mControlMemory = mClient->getControlBlock();
- mSignalServer = sm;
- mControl = static_cast<SharedClient *>(mControlMemory->getBase());
}
SurfaceComposerClient::~SurfaceComposerClient()
{
- VERBOSE("Destroying client %p, conn %p", this, mClient.get());
+ delete mPrebuiltLayerState;
dispose();
}
@@ -188,69 +193,31 @@ sp<IBinder> SurfaceComposerClient::connection() const
return (mClient != 0) ? mClient->asBinder() : 0;
}
-sp<SurfaceComposerClient>
-SurfaceComposerClient::clientForConnection(const sp<IBinder>& conn)
+status_t SurfaceComposerClient::linkToComposerDeath(
+ const sp<IBinder::DeathRecipient>& recipient,
+ void* cookie, uint32_t flags)
{
- sp<SurfaceComposerClient> client;
-
- { // scope for lock
- Mutex::Autolock _l(gLock);
- client = gActiveConnections.valueFor(conn);
- }
-
- if (client == 0) {
- // Need to make a new client.
- sp<ISurfaceComposer> sm(getComposerService());
- client = new SurfaceComposerClient(sm, conn);
- if (client != 0 && client->initCheck() == NO_ERROR) {
- Mutex::Autolock _l(gLock);
- gActiveConnections.add(conn, client);
- //LOGD("we have %d connections", gActiveConnections.size());
- } else {
- client.clear();
- }
- }
-
- return client;
+ sp<ISurfaceComposer> sm(getComposerService());
+ return sm->asBinder()->linkToDeath(recipient, cookie, flags);
}
void SurfaceComposerClient::dispose()
{
// this can be called more than once.
-
- sp<IMemoryHeap> controlMemory;
- sp<ISurfaceFlingerClient> client;
-
- {
- Mutex::Autolock _lg(gLock);
- Mutex::Autolock _lm(mLock);
-
- mSignalServer = 0;
-
- if (mClient != 0) {
- client = mClient;
- mClient.clear();
-
- ssize_t i = gActiveConnections.indexOfKey(client->asBinder());
- if (i >= 0 && gActiveConnections.valueAt(i) == this) {
- VERBOSE("Removing client %p from map at %d", this, int(i));
- gActiveConnections.removeItemsAt(i);
- }
- }
-
- delete mPrebuiltLayerState;
- mPrebuiltLayerState = 0;
- controlMemory = mControlMemory;
- mControlMemory.clear();
- mControl = 0;
- mStatus = NO_INIT;
+ sp<ISurfaceComposerClient> client;
+ Mutex::Autolock _lm(mLock);
+ if (mClient != 0) {
+ Composer::removeClient(this);
+ client = mClient; // hold ref while lock is held
+ mClient.clear();
}
+ mStatus = NO_INIT;
}
status_t SurfaceComposerClient::getDisplayInfo(
DisplayID dpy, DisplayInfo* info)
{
- if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
@@ -268,7 +235,7 @@ status_t SurfaceComposerClient::getDisplayInfo(
ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy)
{
- if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
@@ -277,7 +244,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy)
ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy)
{
- if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
@@ -286,7 +253,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy)
ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy)
{
- if (uint32_t(dpy)>=NUM_DISPLAY_MAX)
+ if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX)
return BAD_VALUE;
volatile surface_flinger_cblk_t const * cblk = get_cblk();
volatile display_cblk_t const * dcblk = cblk->displays + dpy;
@@ -305,12 +272,6 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays()
return n;
}
-
-void SurfaceComposerClient::signalServer()
-{
- mSignalServer->signal();
-}
-
sp<SurfaceControl> SurfaceComposerClient::createSurface(
int pid,
DisplayID display,
@@ -327,7 +288,6 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(
return SurfaceComposerClient::createSurface(pid, name, display,
w, h, format, flags);
-
}
sp<SurfaceControl> SurfaceComposerClient::createSurface(
@@ -341,13 +301,11 @@ sp<SurfaceControl> SurfaceComposerClient::createSurface(
{
sp<SurfaceControl> result;
if (mStatus == NO_ERROR) {
- ISurfaceFlingerClient::surface_data_t data;
+ ISurfaceComposerClient::surface_data_t data;
sp<ISurface> surface = mClient->createSurface(&data, pid, name,
display, w, h, format, flags);
if (surface != 0) {
- if (uint32_t(data.token) < NUM_LAYERS_MAX) {
- result = new SurfaceControl(this, surface, data, w, h, format, flags);
- }
+ result = new SurfaceControl(this, surface, data, w, h, format, flags);
}
}
return result;
@@ -373,56 +331,14 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid)
void SurfaceComposerClient::openGlobalTransaction()
{
- Mutex::Autolock _l(gLock);
-
- if (gOpenTransactions.size()) {
- LOGE("openGlobalTransaction() called more than once. skipping.");
- return;
- }
-
- const size_t N = gActiveConnections.size();
- VERBOSE("openGlobalTransaction (%ld clients)", N);
- for (size_t i=0; i<N; i++) {
- sp<SurfaceComposerClient> client(gActiveConnections.valueAt(i));
- if (gOpenTransactions.indexOf(client) < 0) {
- if (client->openTransaction() == NO_ERROR) {
- if (gOpenTransactions.add(client) < 0) {
- // Ooops!
- LOGE( "Unable to add a SurfaceComposerClient "
- "to the global transaction set (out of memory?)");
- client->closeTransaction();
- // let it go, it'll fail later when the user
- // tries to do something with the transaction
- }
- } else {
- LOGE("openTransaction on client %p failed", client.get());
- // let it go, it'll fail later when the user
- // tries to do something with the transaction
- }
- }
- }
+ Composer::openGlobalTransaction();
}
void SurfaceComposerClient::closeGlobalTransaction()
{
- gLock.lock();
- SortedVector< sp<SurfaceComposerClient> > clients(gOpenTransactions);
- gOpenTransactions.clear();
- gLock.unlock();
-
- const size_t N = clients.size();
- VERBOSE("closeGlobalTransaction (%ld clients)", N);
-
- sp<ISurfaceComposer> sm(getComposerService());
- sm->openGlobalTransaction();
- for (size_t i=0; i<N; i++) {
- clients[i]->closeTransaction();
- }
- sm->closeGlobalTransaction();
-
+ Composer::closeGlobalTransaction();
}
-
status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
{
sp<ISurfaceComposer> sm(getComposerService());
@@ -447,26 +363,16 @@ status_t SurfaceComposerClient::openTransaction()
if (mStatus != NO_ERROR)
return mStatus;
Mutex::Autolock _l(mLock);
- VERBOSE( "openTransaction (client %p, mTransactionOpen=%d)",
- this, mTransactionOpen);
mTransactionOpen++;
- if (mPrebuiltLayerState == 0) {
- mPrebuiltLayerState = new layer_state_t;
- }
return NO_ERROR;
}
-
status_t SurfaceComposerClient::closeTransaction()
{
if (mStatus != NO_ERROR)
return mStatus;
Mutex::Autolock _l(mLock);
-
- VERBOSE( "closeTransaction (client %p, mTransactionOpen=%d)",
- this, mTransactionOpen);
-
if (mTransactionOpen <= 0) {
LOGE( "closeTransaction (client %p, mTransactionOpen=%d) "
"called more times than openTransaction()",
@@ -488,7 +394,7 @@ status_t SurfaceComposerClient::closeTransaction()
return NO_ERROR;
}
-layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index)
+layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index)
{
// API usage error, do nothing.
if (mTransactionOpen<=0) {
@@ -498,7 +404,7 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index)
}
// use mPrebuiltLayerState just to find out if we already have it
- layer_state_t& dummy = *mPrebuiltLayerState;
+ layer_state_t& dummy(*mPrebuiltLayerState);
dummy.surface = index;
ssize_t i = mStates.indexOf(dummy);
if (i < 0) {
@@ -508,49 +414,49 @@ layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index)
return mStates.editArray() + i;
}
-layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id)
+layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id)
{
layer_state_t* s;
mLock.lock();
- s = _get_state_l(id);
+ s = get_state_l(id);
if (!s) mLock.unlock();
return s;
}
-void SurfaceComposerClient::_unlockLayerState()
+void SurfaceComposerClient::unlockLayerState()
{
mLock.unlock();
}
status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::ePositionChanged;
s->x = x;
s->y = y;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eSizeChanged;
s->w = w;
s->h = h;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eLayerChanged;
s->z = z;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
@@ -579,34 +485,34 @@ status_t SurfaceComposerClient::unfreeze(SurfaceID id)
status_t SurfaceComposerClient::setFlags(SurfaceID id,
uint32_t flags, uint32_t mask)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eVisibilityChanged;
s->flags &= ~mask;
s->flags |= (flags & mask);
s->mask |= mask;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
status_t SurfaceComposerClient::setTransparentRegionHint(
SurfaceID id, const Region& transparentRegion)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eTransparentRegionChanged;
s->transparentRegion = transparentRegion;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eAlphaChanged;
s->alpha = alpha;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
@@ -615,7 +521,7 @@ status_t SurfaceComposerClient::setMatrix(
float dsdx, float dtdx,
float dsdy, float dtdy )
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eMatrixChanged;
layer_state_t::matrix22_t matrix;
@@ -624,19 +530,70 @@ status_t SurfaceComposerClient::setMatrix(
matrix.dsdy = dsdy;
matrix.dtdy = dtdy;
s->matrix = matrix;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint)
{
- layer_state_t* s = _lockLayerState(id);
+ layer_state_t* s = lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eFreezeTintChanged;
s->tint = tint;
- _unlockLayerState();
+ unlockLayerState();
return NO_ERROR;
}
+// ----------------------------------------------------------------------------
+
+ScreenshotClient::ScreenshotClient()
+ : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
+}
+
+status_t ScreenshotClient::update() {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, 0, 0);
+}
+
+status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) {
+ sp<ISurfaceComposer> s(ComposerService::getComposerService());
+ if (s == NULL) return NO_INIT;
+ mHeap = 0;
+ return s->captureScreen(0, &mHeap,
+ &mWidth, &mHeight, &mFormat, reqWidth, reqHeight);
+}
+
+void ScreenshotClient::release() {
+ mHeap = 0;
+}
+
+void const* ScreenshotClient::getPixels() const {
+ return mHeap->getBase();
+}
+
+uint32_t ScreenshotClient::getWidth() const {
+ return mWidth;
+}
+
+uint32_t ScreenshotClient::getHeight() const {
+ return mHeight;
+}
+
+PixelFormat ScreenshotClient::getFormat() const {
+ return mFormat;
+}
+
+uint32_t ScreenshotClient::getStride() const {
+ return mWidth;
+}
+
+size_t ScreenshotClient::getSize() const {
+ return mHeap->getSize();
+}
+
+// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/surfaceflinger/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk
index 5053e7d..5053e7d 100644
--- a/libs/surfaceflinger/tests/Android.mk
+++ b/libs/surfaceflinger_client/tests/Android.mk
diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk
index 24c2d01..d3dfe04 100644
--- a/libs/surfaceflinger/tests/resize/Android.mk
+++ b/libs/surfaceflinger_client/tests/SharedBufferStack/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- resize.cpp
+ SharedBufferStackTest.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -10,7 +10,7 @@ LOCAL_SHARED_LIBRARIES := \
libui \
libsurfaceflinger_client
-LOCAL_MODULE:= test-resize
+LOCAL_MODULE:= test-sharedbufferstack
LOCAL_MODULE_TAGS := tests
diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
new file mode 100644
index 0000000..f409f48
--- /dev/null
+++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp
@@ -0,0 +1,279 @@
+/*
+ * 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.
+ */
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <cutils/memory.h>
+#include <cutils/log.h>
+#include <utils/Errors.h>
+#include <private/surfaceflinger/SharedBufferStack.h>
+
+using namespace android;
+
+void log(const char* prefix, int *b, size_t num);
+void test0(SharedBufferServer& s, SharedBufferClient& c, size_t num, int* list);
+
+// ----------------------------------------------------------------------------
+
+int main(int argc, char** argv)
+{
+ SharedClient client;
+ SharedBufferServer s(&client, 0, 4, 0);
+ SharedBufferClient c(&client, 0, 4, 0);
+
+ printf("basic test 0\n");
+ int list0[4] = {0, 1, 2, 3};
+ test0(s, c, 4, list0);
+
+ printf("basic test 1\n");
+ int list1[4] = {2, 1, 0, 3};
+ test0(s, c, 4, list1);
+
+ int b = c.dequeue();
+ c.lock(b);
+ c.queue(b);
+ s.retireAndLock();
+
+ printf("basic test 2\n");
+ int list2[4] = {1, 2, 3, 0};
+ test0(s, c, 4, list2);
+
+
+ printf("resize test\n");
+ class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback {
+ SharedBufferServer& s;
+ virtual status_t operator()(int bufferCount) const {
+ return s.resize(bufferCount);
+ }
+ public:
+ SetBufferCountIPC(SharedBufferServer& s) : s(s) { }
+ } resize(s);
+
+ c.setBufferCount(6, resize);
+ int list3[6] = {3, 2, 1, 4, 5, 0};
+ test0(s, c, 6, list3);
+
+ return 0;
+}
+
+void log(const char* prefix, int *b, size_t num)
+{
+ printf("%s: ", prefix);
+ for (size_t i=0 ; i<num ; i++) {
+ printf("%d ", b[i]);
+ }
+ printf("\n");
+}
+
+// ----------------------------------------------------------------------------
+
+void test0(
+ SharedBufferServer& s,
+ SharedBufferClient& c,
+ size_t num,
+ int* list)
+{
+ status_t err;
+ int b[num], u[num], r[num];
+
+ for (size_t i=0 ; i<num ; i++) {
+ b[i] = c.dequeue();
+ assert(b[i]==list[i]);
+ }
+ log("DQ", b, num);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.lock(b[i]);
+ assert(err==0);
+ }
+ log("LK", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.queue(b[i]);
+ assert(err==0);
+ }
+ log(" Q", b, num-1);
+
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ r[i] = s.retireAndLock();
+ assert(r[i]==list[i]);
+ err = s.unlock(r[i]);
+ assert(err == 0);
+ }
+ log("RT", r, num-1);
+
+ err = c.lock(b[num-1]);
+ assert(err == 0);
+ log("LK", b+num-1, 1);
+
+ err = c.queue(b[num-1]);
+ assert(err == 0);
+ log(" Q", b+num-1, 1);
+
+ r[num-1] = s.retireAndLock();
+ assert(r[num-1]==list[num-1]);
+ err = s.unlock(r[num-1]);
+ assert(err == 0);
+ log("RT", r+num-1, 1);
+
+ // ------------------------------------
+ printf("\n");
+
+ for (size_t i=0 ; i<num ; i++) {
+ b[i] = c.dequeue();
+ assert(b[i]==list[i]);
+ }
+ log("DQ", b, num);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.lock(b[i]);
+ assert(err==0);
+ }
+ log("LK", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ u[i] = b[num-2-i];
+ }
+ u[num-1] = b[num-1];
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.queue(u[i]);
+ assert(err==0);
+ }
+ log(" Q", u, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ r[i] = s.retireAndLock();
+ assert(r[i]==u[i]);
+ err = s.unlock(r[i]);
+ assert(err == 0);
+ }
+ log("RT", r, num-1);
+
+ err = c.lock(b[num-1]);
+ assert(err == 0);
+ log("LK", b+num-1, 1);
+
+ err = c.queue(b[num-1]);
+ assert(err == 0);
+ log(" Q", b+num-1, 1);
+
+ r[num-1] = s.retireAndLock();
+ assert(r[num-1]==list[num-1]);
+ err = s.unlock(r[num-1]);
+ assert(err == 0);
+ log("RT", r+num-1, 1);
+
+ // ------------------------------------
+ printf("\n");
+
+ for (size_t i=0 ; i<num ; i++) {
+ b[i] = c.dequeue();
+ assert(b[i]==u[i]);
+ }
+ log("DQ", b, num);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.lock(b[i]);
+ assert(err==0);
+ }
+ log("LK", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.queue(b[i]);
+ assert(err==0);
+ }
+ log(" Q", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ r[i] = s.retireAndLock();
+ assert(r[i]==u[i]);
+ err = s.unlock(r[i]);
+ assert(err == 0);
+ }
+ log("RT", r, num-1);
+
+ err = c.lock(u[num-1]);
+ assert(err == 0);
+ log("LK", u+num-1, 1);
+
+ err = c.queue(u[num-1]);
+ assert(err == 0);
+ log(" Q", u+num-1, 1);
+
+ r[num-1] = s.retireAndLock();
+ assert(r[num-1]==u[num-1]);
+ err = s.unlock(r[num-1]);
+ assert(err == 0);
+ log("RT", r+num-1, 1);
+
+ // ------------------------------------
+ printf("\n");
+
+ b[0] = c.dequeue();
+ assert(b[0]==u[0]);
+ log("DQ", b, 1);
+
+ c.undoDequeue(b[0]);
+ assert(err == 0);
+ log("UDQ", b, 1);
+
+ // ------------------------------------
+ printf("\n");
+
+ for (size_t i=0 ; i<num ; i++) {
+ b[i] = c.dequeue();
+ assert(b[i]==u[i]);
+ }
+ log("DQ", b, num);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.lock(b[i]);
+ assert(err==0);
+ }
+ log("LK", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ err = c.queue(b[i]);
+ assert(err==0);
+ }
+ log(" Q", b, num-1);
+
+ for (size_t i=0 ; i<num-1 ; i++) {
+ r[i] = s.retireAndLock();
+ assert(r[i]==u[i]);
+ err = s.unlock(r[i]);
+ assert(err == 0);
+ }
+ log("RT", r, num-1);
+
+ err = c.lock(u[num-1]);
+ assert(err == 0);
+ log("LK", u+num-1, 1);
+
+ err = c.queue(u[num-1]);
+ assert(err == 0);
+ log(" Q", u+num-1, 1);
+
+ r[num-1] = s.retireAndLock();
+ assert(r[num-1]==u[num-1]);
+ err = s.unlock(r[num-1]);
+ assert(err == 0);
+ log("RT", r+num-1, 1);
+ printf("\n");
+}
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index f7acd97..c4a09d6 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -9,8 +9,14 @@ LOCAL_SRC_FILES:= \
GraphicBuffer.cpp \
GraphicBufferAllocator.cpp \
GraphicBufferMapper.cpp \
+ GraphicLog.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
+ Input.cpp \
+ InputDispatcher.cpp \
+ InputManager.cpp \
+ InputReader.cpp \
+ InputTransport.cpp \
IOverlay.cpp \
Overlay.cpp \
PixelFormat.cpp \
@@ -33,3 +39,13 @@ ifeq ($(TARGET_SIMULATOR),true)
endif
include $(BUILD_SHARED_LIBRARY)
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index d45eaf0..41daa9c 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -54,10 +54,12 @@
*/
#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8)))
+/* this macro computes the number of bytes needed to represent a bit array of the specified size */
+#define sizeof_bit_array(bits) ((bits + 7) / 8)
+
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
-#define id_to_index(id) ((id&ID_MASK)+1)
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
@@ -71,6 +73,10 @@
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#endif
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+
namespace android {
static const char *WAKE_LOCK_ID = "KeyEvents";
@@ -82,9 +88,13 @@ static inline int max(int v1, int v2)
return (v1 > v2) ? v1 : v2;
}
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+ , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -96,7 +106,8 @@ EventHub::EventHub(void)
: mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
- , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+ , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
+ , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
@@ -121,7 +132,7 @@ status_t EventHub::errorCheck() const
String8 EventHub::getDeviceName(int32_t deviceId) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return String8();
return device->name;
}
@@ -129,106 +140,76 @@ String8 EventHub::getDeviceName(int32_t deviceId) const
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return 0;
return device->classes;
}
-int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
- int* outMaxValue, int* outFlat, int* outFuzz) const
-{
+status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ outAxisInfo->clear();
+
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device == NULL) return -1;
struct input_absinfo info;
- if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
- LOGE("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
- return -1;
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
+ LOGW("Error reading absolute controller %d for device %s fd %d\n",
+ axis, device->name.string(), device->fd);
+ return -errno;
}
- *outMinValue = info.minimum;
- *outMaxValue = info.maximum;
- *outFlat = info.flat;
- *outFuzz = info.fuzz;
- return 0;
-}
-int EventHub::getSwitchState(int sw) const
-{
-#ifdef EV_SW
- if (sw >= 0 && sw <= SW_MAX) {
- int32_t devid = mSwitches[sw];
- if (devid != 0) {
- return getSwitchState(devid, sw);
- }
+ if (info.minimum != info.maximum) {
+ outAxisInfo->valid = true;
+ outAxisInfo->minValue = info.minimum;
+ outAxisInfo->maxValue = info.maximum;
+ outAxisInfo->flat = info.flat;
+ outAxisInfo->fuzz = info.fuzz;
}
-#endif
- return -1;
+ return OK;
}
-int EventHub::getSwitchState(int32_t deviceId, int sw) const
-{
-#ifdef EV_SW
- AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
- if (device == NULL) return -1;
-
- if (sw >= 0 && sw <= SW_MAX) {
- uint8_t sw_bitmask[(SW_MAX+7)/8];
- memset(sw_bitmask, 0, sizeof(sw_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
- return test_bit(sw, sw_bitmask) ? 1 : 0;
+int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ if (scanCode >= 0 && scanCode <= KEY_MAX) {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDeviceLocked(deviceId);
+ if (device != NULL) {
+ return getScanCodeStateLocked(device, scanCode);
}
}
-#endif
-
- return -1;
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getScancodeState(int code) const
-{
- return getScancodeState(mFirstKeyboardId, code);
+int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
+ memset(key_bitmask, 0, sizeof(key_bitmask));
+ if (ioctl(device->fd,
+ EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getScancodeState(int32_t deviceId, int code) const
-{
+int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
- if (device == NULL) return -1;
-
- if (code >= 0 && code <= KEY_MAX) {
- uint8_t key_bitmask[(KEY_MAX+7)/8];
- memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
- return test_bit(code, key_bitmask) ? 1 : 0;
- }
- }
-
- return -1;
-}
-int EventHub::getKeycodeState(int code) const
-{
- return getKeycodeState(mFirstKeyboardId, code);
+ device_t* device = getDeviceLocked(deviceId);
+ if (device != NULL) {
+ return getKeyCodeStateLocked(device, keyCode);
+ }
+ return AKEY_STATE_UNKNOWN;
}
-int EventHub::getKeycodeState(int32_t deviceId, int code) const
-{
- AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
- if (device == NULL || device->layoutMap == NULL) return -1;
-
+int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
Vector<int32_t> scanCodes;
- device->layoutMap->findScancodes(code, &scanCodes);
-
- uint8_t key_bitmask[(KEY_MAX+7)/8];
+ device->layoutMap->findScancodes(keyCode, &scanCodes);
+
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
#if 0
for (size_t i=0; i<=KEY_MAX; i++) {
LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -239,19 +220,79 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const
int32_t sc = scanCodes.itemAt(i);
//LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask));
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) {
- return 1;
+ return AKEY_STATE_DOWN;
}
}
+ return AKEY_STATE_UP;
}
-
- return 0;
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
+#ifdef EV_SW
+ if (sw >= 0 && sw <= SW_MAX) {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDeviceLocked(deviceId);
+ if (device != NULL) {
+ return getSwitchStateLocked(device, sw);
+ }
+ }
+#endif
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
+ uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
+ memset(sw_bitmask, 0, sizeof(sw_bitmask));
+ if (ioctl(device->fd,
+ EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
+ return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
+ }
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ AutoMutex _l(mLock);
+
+ device_t* device = getDeviceLocked(deviceId);
+ if (device != NULL) {
+ return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags);
+ }
+ return false;
+}
+
+bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) const {
+ if (device->layoutMap == NULL || device->keyBitmask == NULL) {
+ return false;
+ }
+
+ Vector<int32_t> scanCodes;
+ for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
+ scanCodes.clear();
+
+ status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
+ if (! err) {
+ // check the possible scan codes identified by the layout map against the
+ // map of codes actually emitted by the driver
+ for (size_t sc = 0; sc < scanCodes.size(); sc++) {
+ if (test_bit(scanCodes[sc], device->keyBitmask)) {
+ outFlags[codeIndex] = 1;
+ break;
+ }
+ }
+ }
+ }
+ return true;
}
status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const
{
AutoMutex _l(mLock);
- device_t* device = getDevice(deviceId);
+ device_t* device = getDeviceLocked(deviceId);
if (device != NULL && device->layoutMap != NULL) {
status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -261,7 +302,7 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
}
if (mHaveFirstKeyboard) {
- device = getDevice(mFirstKeyboardId);
+ device = getDeviceLocked(mFirstKeyboardId);
if (device != NULL && device->layoutMap != NULL) {
status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
@@ -278,11 +319,13 @@ status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
void EventHub::addExcludedDevice(const char* deviceName)
{
+ AutoMutex _l(mLock);
+
String8 name(deviceName);
mExcludedDevices.push_back(name);
}
-EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
+EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
{
if (deviceId == 0) deviceId = mFirstKeyboardId;
int32_t id = deviceId & ID_MASK;
@@ -295,27 +338,15 @@ EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
return NULL;
}
-bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
- int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
- int32_t* outValue, nsecs_t* outWhen)
+bool EventHub::getEvent(RawEvent* outEvent)
{
- *outDeviceId = 0;
- *outType = 0;
- *outScancode = 0;
- *outKeycode = 0;
- *outFlags = 0;
- *outValue = 0;
- *outWhen = 0;
-
- status_t err;
-
- fd_set readfds;
- int maxFd = -1;
- int cc;
- int i;
- int res;
- int pollres;
- struct input_event iev;
+ outEvent->deviceId = 0;
+ outEvent->type = 0;
+ outEvent->scanCode = 0;
+ outEvent->keyCode = 0;
+ outEvent->flags = 0;
+ outEvent->value = 0;
+ outEvent->when = 0;
// Note that we only allow one caller to getEvent(), so don't need
// to do locking here... only when adding/removing devices.
@@ -323,94 +354,145 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
if (!mOpened) {
mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
mOpened = true;
+ mNeedToSendFinishedDeviceScan = true;
}
- while(1) {
-
- // First, report any devices that had last been added/removed.
+ for (;;) {
+ // Report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
device_t* device = mClosingDevices;
LOGV("Reporting device closed: id=0x%x, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_REMOVED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_REMOVED;
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
delete device;
+ mNeedToSendFinishedDeviceScan = true;
return true;
}
+
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
- *outDeviceId = device->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = DEVICE_ADDED;
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = DEVICE_ADDED;
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mNeedToSendFinishedDeviceScan = true;
return true;
}
- release_wake_lock(WAKE_LOCK_ID);
+ if (mNeedToSendFinishedDeviceScan) {
+ mNeedToSendFinishedDeviceScan = false;
+ outEvent->type = FINISHED_DEVICE_SCAN;
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return true;
+ }
- pollres = poll(mFDs, mFDCount, -1);
+ // Grab the next input event.
+ for (;;) {
+ // Consume buffered input events, if any.
+ if (mInputBufferIndex < mInputBufferCount) {
+ const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
+ const device_t* device = mDevices[mInputDeviceIndex];
+
+ LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
+ (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = iev.type;
+ outEvent->scanCode = iev.code;
+ if (iev.type == EV_KEY) {
+ status_t err = device->layoutMap->map(iev.code,
+ & outEvent->keyCode, & outEvent->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, outEvent->keyCode, outEvent->flags, err);
+ if (err != 0) {
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ outEvent->flags = 0;
+ }
+ } else {
+ outEvent->keyCode = iev.code;
+ }
+ outEvent->value = iev.value;
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+ // Use an event timestamp in the same timebase as
+ // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+ // as expected by the rest of the system.
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return true;
+ }
- if (pollres <= 0) {
- if (errno != EINTR) {
- LOGW("select failed (errno=%d)\n", errno);
- usleep(100000);
+ // Finish reading all events from devices identified in previous poll().
+ // This code assumes that mInputDeviceIndex is initially 0 and that the
+ // revents member of pollfd is initialized to 0 when the device is first added.
+ // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
+ mInputDeviceIndex += 1;
+ if (mInputDeviceIndex >= mFDCount) {
+ break;
}
- continue;
- }
- //printf("poll %d, returned %d\n", mFDCount, pollres);
-
- // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
- for(i = 1; i < mFDCount; i++) {
- if(mFDs[i].revents) {
- LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
- if(mFDs[i].revents & POLLIN) {
- res = read(mFDs[i].fd, &iev, sizeof(iev));
- if (res == sizeof(iev)) {
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
- mDevices[i]->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec,
- iev.type, iev.code, iev.value);
- *outDeviceId = mDevices[i]->id;
- if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
- *outType = iev.type;
- *outScancode = iev.code;
- if (iev.type == EV_KEY) {
- err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
- LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
- iev.code, *outKeycode, *outFlags, err);
- if (err != 0) {
- *outKeycode = 0;
- *outFlags = 0;
- }
- } else {
- *outKeycode = iev.code;
- }
- *outValue = iev.value;
- *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
- return true;
- } else {
- if (res<0) {
- LOGW("could not get event (errno=%d)", errno);
- } else {
- LOGE("could not get event (wrong size: %d)", res);
- }
- continue;
+ const struct pollfd& pfd = mFDs[mInputDeviceIndex];
+ if (pfd.revents & POLLIN) {
+ int32_t readSize = read(pfd.fd, mInputBufferData,
+ sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+ if (readSize < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ LOGW("could not get event (errno=%d)", errno);
}
+ } else if ((readSize % sizeof(struct input_event)) != 0) {
+ LOGE("could not get event (wrong size: %d)", readSize);
+ } else {
+ mInputBufferCount = readSize / sizeof(struct input_event);
+ mInputBufferIndex = 0;
}
}
}
-
- // read_notify() will modify mFDs and mFDCount, so this must be done after
+
+#if HAVE_INOTIFY
+ // readNotify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
- read_notify(mFDs[0].fd);
+ readNotify(mFDs[0].fd);
+ mFDs[0].revents = 0;
+ continue; // report added or removed devices immediately
+ }
+#endif
+
+ mInputDeviceIndex = 0;
+
+ // Poll for events. Mind the wake lock dance!
+ // We hold a wake lock at all times except during poll(). This works due to some
+ // subtle choreography. When a device driver has pending (unread) events, it acquires
+ // a kernel wake lock. However, once the last pending event has been read, the device
+ // driver will release the kernel wake lock. To prevent the system from going to sleep
+ // when this happens, the EventHub holds onto its own user wake lock while the client
+ // is processing events. Thus the system can only sleep if there are no events
+ // pending or currently being processed.
+ release_wake_lock(WAKE_LOCK_ID);
+
+ int pollResult = poll(mFDs, mFDCount, -1);
+
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
+ if (pollResult <= 0) {
+ if (errno != EINTR) {
+ LOGW("poll failed (errno=%d)\n", errno);
+ usleep(100000);
+ }
}
}
}
@@ -429,6 +511,7 @@ bool EventHub::openPlatformInput(void)
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
+ mFDs[0].revents = 0;
mDevices[0] = NULL;
#ifdef HAVE_INOTIFY
mFDs[0].fd = inotify_init();
@@ -444,49 +527,37 @@ bool EventHub::openPlatformInput(void)
mFDs[0].fd = -1;
#endif
- res = scan_dir(device_path);
+ res = scanDir(device_path);
if(res < 0) {
LOGE("scan dir failed for %s\n", device_path);
- //open_device("/dev/input/event0");
}
return true;
}
-/*
- * Inspect the known devices to determine whether physical keys exist for the given
- * framework-domain key codes.
- */
-bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) {
- for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
- outFlags[codeIndex] = 0;
-
- // check each available hardware device for support for this keycode
- Vector<int32_t> scanCodes;
- for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) {
- if (mDevices[n]) {
- status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
- if (!err) {
- // check the possible scan codes identified by the layout map against the
- // map of codes actually emitted by the driver
- for (size_t sc = 0; sc < scanCodes.size(); sc++) {
- if (test_bit(scanCodes[sc], mDevices[n]->keyBitmask)) {
- outFlags[codeIndex] = 1;
- break;
- }
- }
- }
- }
+// ----------------------------------------------------------------------------
+
+static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) {
+ const uint8_t* end = array + endIndex;
+ array += startIndex;
+ while (array != end) {
+ if (*(array++) != 0) {
+ return true;
}
}
-
- return true;
+ return false;
}
-// ----------------------------------------------------------------------------
+static const int32_t GAMEPAD_KEYCODES[] = {
+ AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C,
+ AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z,
+ AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1,
+ AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2,
+ AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR,
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE
+};
-int EventHub::open_device(const char *deviceName)
-{
+int EventHub::openDevice(const char *deviceName) {
int version;
int fd;
struct pollfd *new_mFDs;
@@ -531,7 +602,6 @@ int EventHub::open_device(const char *deviceName)
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s\n", deviceName, test);
close(fd);
- fd = -1;
return -1;
}
}
@@ -545,6 +615,12 @@ int EventHub::open_device(const char *deviceName)
idstr[0] = '\0';
}
+ if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+ LOGE("Error %d making device file descriptor non-blocking.", errno);
+ close(fd);
+ return -1;
+ }
+
int devid = 0;
while (devid < mNumDevicesById) {
if (mDevicesById[devid].device == NULL) {
@@ -599,30 +675,32 @@ int EventHub::open_device(const char *deviceName)
return -1;
}
+ device->fd = fd;
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
+ mFDs[mFDCount].revents = 0;
- // figure out the kinds of events the device reports
+ // Figure out the kinds of events the device reports.
- // See if this is a keyboard, and classify it. Note that we only
- // consider up through the function keys; we don't want to include
- // ones after that (play cd etc) so we don't mistakenly consider a
- // controller to be a keyboard.
- uint8_t key_bitmask[(KEY_MAX+7)/8];
+ uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
+
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP\n");
- //for (int i=0; i<((KEY_MAX+7)/8); i++) {
+ //for (int i = 0; i < sizeof(key_bitmask); i++) {
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
//}
- for (int i=0; i<((BTN_MISC+7)/8); i++) {
- if (key_bitmask[i] != 0) {
- device->classes |= CLASS_KEYBOARD;
- break;
- }
- }
- if ((device->classes & CLASS_KEYBOARD) != 0) {
+
+ // See if this is a keyboard. Ignore everything in the button range except for
+ // gamepads which are also considered keyboards.
+ if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),
+ sizeof_bit_array(BTN_DIGI))
+ || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),
+ sizeof_bit_array(KEY_MAX + 1))) {
+ device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
+
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
@@ -634,53 +712,57 @@ int EventHub::open_device(const char *deviceName)
}
}
- // See if this is a trackball.
+ // See if this is a trackball (or mouse).
if (test_bit(BTN_MOUSE, key_bitmask)) {
- uint8_t rel_bitmask[(REL_MAX+7)/8];
+ uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
LOGV("Getting relative controllers...");
- if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
- {
+ if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
- device->classes |= CLASS_TRACKBALL;
+ device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
}
}
}
-
- uint8_t abs_bitmask[(ABS_MAX+7)/8];
+
+ // See if this is a touch pad.
+ uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
- ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
-
- // Is this a new modern multi-touch driver?
- if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
- && test_bit(ABS_MT_POSITION_X, abs_bitmask)
- && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
- device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;
-
- // Is this an old style single-touch driver?
- } else if (test_bit(BTN_TOUCH, key_bitmask)
- && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
- device->classes |= CLASS_TOUCHSCREEN;
+ if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {
+ // Is this a new modern multi-touch driver?
+ if (test_bit(ABS_MT_POSITION_X, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
+
+ // Is this an old style single-touch driver?
+ } else if (test_bit(BTN_TOUCH, key_bitmask)
+ && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
+ device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
+ }
}
#ifdef EV_SW
// figure out the switches this device reports
- uint8_t sw_bitmask[(SW_MAX+7)/8];
+ uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
+ bool hasSwitches = false;
if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) {
for (int i=0; i<EV_SW; i++) {
//LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask));
if (test_bit(i, sw_bitmask)) {
+ hasSwitches = true;
if (mSwitches[i] == 0) {
mSwitches[i] = device->id;
}
}
}
}
+ if (hasSwitches) {
+ device->classes |= INPUT_DEVICE_CLASS_SWITCH;
+ }
#endif
- if ((device->classes&CLASS_KEYBOARD) != 0) {
+ if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
char tmpfn[sizeof(name)];
char keylayoutFilename[300];
@@ -702,7 +784,10 @@ int EventHub::open_device(const char *deviceName)
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
- device->layoutMap->load(keylayoutFilename);
+ status_t status = device->layoutMap->load(keylayoutFilename);
+ if (status) {
+ LOGE("Error %d loading key layout.", status);
+ }
// tell the world about the devname (the descriptive name)
if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
@@ -722,23 +807,39 @@ int EventHub::open_device(const char *deviceName)
property_set(propName, name);
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (hasKeycode(device, kKeyCodeQ)) {
- device->classes |= CLASS_ALPHAKEY;
+ if (hasKeycodeLocked(device, AKEYCODE_Q)) {
+ device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
- // See if this has a DPAD.
- if (hasKeycode(device, kKeyCodeDpadUp) &&
- hasKeycode(device, kKeyCodeDpadDown) &&
- hasKeycode(device, kKeyCodeDpadLeft) &&
- hasKeycode(device, kKeyCodeDpadRight) &&
- hasKeycode(device, kKeyCodeDpadCenter)) {
- device->classes |= CLASS_DPAD;
+ // See if this device has a DPAD.
+ if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
+ hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
+ device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
+ // See if this device has a gamepad.
+ for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
+ if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
+ device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
+ break;
+ }
+ }
+
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
device->id, name, propName, keylayoutFilename);
}
+ // If the device isn't recognized as something we handle, don't monitor it.
+ if (device->classes == 0) {
+ LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+ close(fd);
+ delete device;
+ return -1;
+ }
+
LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
@@ -754,7 +855,7 @@ int EventHub::open_device(const char *deviceName)
return 0;
}
-bool EventHub::hasKeycode(device_t* device, int keycode) const
+bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
{
if (device->keyBitmask == NULL || device->layoutMap == NULL) {
return false;
@@ -773,10 +874,9 @@ bool EventHub::hasKeycode(device_t* device, int keycode) const
return false;
}
-int EventHub::close_device(const char *deviceName)
-{
+int EventHub::closeDevice(const char *deviceName) {
AutoMutex _l(mLock);
-
+
int i;
for(i = 1; i < mFDCount; i++) {
if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
@@ -826,8 +926,7 @@ int EventHub::close_device(const char *deviceName)
return -1;
}
-int EventHub::read_notify(int nfd)
-{
+int EventHub::readNotify(int nfd) {
#ifdef HAVE_INOTIFY
int res;
char devname[PATH_MAX];
@@ -837,7 +936,7 @@ int EventHub::read_notify(int nfd)
int event_pos = 0;
struct inotify_event *event;
- LOGV("EventHub::read_notify nfd: %d\n", nfd);
+ LOGV("EventHub::readNotify nfd: %d\n", nfd);
res = read(nfd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
@@ -857,10 +956,10 @@ int EventHub::read_notify(int nfd)
if(event->len) {
strcpy(filename, event->name);
if(event->mask & IN_CREATE) {
- open_device(devname);
+ openDevice(devname);
}
else {
- close_device(devname);
+ closeDevice(devname);
}
}
event_size = sizeof(*event) + event->len;
@@ -872,7 +971,7 @@ int EventHub::read_notify(int nfd)
}
-int EventHub::scan_dir(const char *dirname)
+int EventHub::scanDir(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
@@ -890,10 +989,38 @@ int EventHub::scan_dir(const char *dirname)
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
- open_device(devname);
+ openDevice(devname);
}
closedir(dir);
return 0;
}
+void EventHub::dump(String8& dump) {
+ dump.append("Event Hub State:\n");
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard));
+ dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId);
+
+ dump.append(INDENT "Devices:\n");
+
+ for (int i = 0; i < mNumDevicesById; i++) {
+ const device_t* device = mDevicesById[i].device;
+ if (device) {
+ if (mFirstKeyboardId == device->id) {
+ dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n",
+ device->id, device->name.string());
+ } else {
+ dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string());
+ }
+ dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
+ dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
+ dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string());
+ }
+ }
+ } // release lock
+}
+
}; // namespace android
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 52380a0..a36d555 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -29,6 +29,7 @@
#include <ui/Rect.h>
#include <ui/FramebufferNativeWindow.h>
+#include <ui/GraphicLog.h>
#include <EGL/egl.h>
@@ -67,7 +68,7 @@ private:
* This implements the (main) framebuffer management. This class is used
* mostly by SurfaceFlinger, but also by command line GL application.
*
- * In fact this is an implementation of android_native_window_t on top of
+ * In fact this is an implementation of ANativeWindow on top of
* the framebuffer.
*
* Currently it is pretty simple, it manages only two buffers (the front and
@@ -117,23 +118,23 @@ FramebufferNativeWindow::FramebufferNativeWindow()
LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",
fbDev->width, fbDev->height, strerror(-err));
- const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags;
- const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi;
- const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi;
- const_cast<int&>(android_native_window_t::minSwapInterval) =
+ const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
+ const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
+ const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
+ const_cast<int&>(ANativeWindow::minSwapInterval) =
fbDev->minSwapInterval;
- const_cast<int&>(android_native_window_t::maxSwapInterval) =
+ const_cast<int&>(ANativeWindow::maxSwapInterval) =
fbDev->maxSwapInterval;
} else {
LOGE("Couldn't get gralloc module");
}
- android_native_window_t::setSwapInterval = setSwapInterval;
- android_native_window_t::dequeueBuffer = dequeueBuffer;
- android_native_window_t::lockBuffer = lockBuffer;
- android_native_window_t::queueBuffer = queueBuffer;
- android_native_window_t::query = query;
- android_native_window_t::perform = perform;
+ ANativeWindow::setSwapInterval = setSwapInterval;
+ ANativeWindow::dequeueBuffer = dequeueBuffer;
+ ANativeWindow::lockBuffer = lockBuffer;
+ ANativeWindow::queueBuffer = queueBuffer;
+ ANativeWindow::query = query;
+ ANativeWindow::perform = perform;
}
FramebufferNativeWindow::~FramebufferNativeWindow()
@@ -168,63 +169,91 @@ status_t FramebufferNativeWindow::compositionComplete()
}
int FramebufferNativeWindow::setSwapInterval(
- android_native_window_t* window, int interval)
+ ANativeWindow* window, int interval)
{
framebuffer_device_t* fb = getSelf(window)->fbDev;
return fb->setSwapInterval(fb, interval);
}
-int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window,
+// only for debugging / logging
+int FramebufferNativeWindow::getCurrentBufferIndex() const
+{
+ Mutex::Autolock _l(mutex);
+ const int index = mCurrentBufferIndex;
+ return index;
+}
+
+int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
android_native_buffer_t** buffer)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
framebuffer_device_t* fb = self->fbDev;
+ int index = self->mBufferHead++;
+ if (self->mBufferHead >= self->mNumBuffers)
+ self->mBufferHead = 0;
+
+ GraphicLog& logger(GraphicLog::getInstance());
+ logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index);
+
// wait for a free buffer
while (!self->mNumFreeBuffers) {
self->mCondition.wait(self->mutex);
}
// get this buffer
self->mNumFreeBuffers--;
- int index = self->mBufferHead++;
- if (self->mBufferHead >= self->mNumBuffers)
- self->mBufferHead = 0;
+ self->mCurrentBufferIndex = index;
*buffer = self->buffers[index].get();
+ logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index);
return 0;
}
-int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::lockBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
+ const int index = self->mCurrentBufferIndex;
+ GraphicLog& logger(GraphicLog::getInstance());
+ logger.log(GraphicLog::SF_FB_LOCK_BEFORE, index);
+
// wait that the buffer we're locking is not front anymore
while (self->front == buffer) {
self->mCondition.wait(self->mutex);
}
+ logger.log(GraphicLog::SF_FB_LOCK_AFTER, index);
+
return NO_ERROR;
}
-int FramebufferNativeWindow::queueBuffer(android_native_window_t* window,
+int FramebufferNativeWindow::queueBuffer(ANativeWindow* window,
android_native_buffer_t* buffer)
{
FramebufferNativeWindow* self = getSelf(window);
Mutex::Autolock _l(self->mutex);
framebuffer_device_t* fb = self->fbDev;
buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;
+
+ const int index = self->mCurrentBufferIndex;
+ GraphicLog& logger(GraphicLog::getInstance());
+ logger.log(GraphicLog::SF_FB_POST_BEFORE, index);
+
int res = fb->post(fb, handle);
+
+ logger.log(GraphicLog::SF_FB_POST_AFTER, index);
+
self->front = static_cast<NativeBuffer*>(buffer);
self->mNumFreeBuffers++;
self->mCondition.broadcast();
return res;
}
-int FramebufferNativeWindow::query(android_native_window_t* window,
+int FramebufferNativeWindow::query(ANativeWindow* window,
int what, int* value)
{
FramebufferNativeWindow* self = getSelf(window);
@@ -245,7 +274,7 @@ int FramebufferNativeWindow::query(android_native_window_t* window,
return BAD_VALUE;
}
-int FramebufferNativeWindow::perform(android_native_window_t* window,
+int FramebufferNativeWindow::perform(ANativeWindow* window,
int operation, ...)
{
switch (operation) {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index ba1fd9c..519c277 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -38,7 +38,7 @@ namespace android {
GraphicBuffer::GraphicBuffer()
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -51,7 +51,7 @@ GraphicBuffer::GraphicBuffer()
GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
PixelFormat reqFormat, uint32_t reqUsage)
: BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width =
height =
@@ -67,7 +67,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h,
uint32_t inStride, native_handle_t* inHandle, bool keepOwnership)
: BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
mBufferMapper(GraphicBufferMapper::get()),
- mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)
+ mInitCheck(NO_ERROR), mIndex(-1)
{
width = w;
height = h;
@@ -111,6 +111,9 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
if (mOwner != ownData)
return INVALID_OPERATION;
+ if (handle && w==width && h==height && f==format && reqUsage==usage)
+ return NO_ERROR;
+
if (handle) {
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
@@ -122,9 +125,6 @@ status_t GraphicBuffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
uint32_t reqUsage)
{
- if (format == PIXEL_FORMAT_RGBX_8888)
- format = PIXEL_FORMAT_RGBA_8888;
-
GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride);
if (err == NO_ERROR) {
@@ -132,7 +132,6 @@ status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
this->height = h;
this->format = format;
this->usage = reqUsage;
- mVStride = 0;
}
return err;
}
@@ -173,7 +172,6 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage)
sur->height = height;
sur->stride = stride;
sur->format = format;
- sur->vstride = mVStride;
sur->data = static_cast<GGLubyte*>(vaddr);
}
return res;
@@ -267,14 +265,6 @@ int GraphicBuffer::getIndex() const {
return mIndex;
}
-void GraphicBuffer::setVerticalStride(uint32_t vstride) {
- mVStride = vstride;
-}
-
-uint32_t GraphicBuffer::getVerticalStride() const {
- return mVStride;
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 6ae7e74..d51664d 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -15,6 +15,8 @@
** limitations under the License.
*/
+#define LOG_TAG "GraphicBufferAllocator"
+
#include <cutils/log.h>
#include <utils/Singleton.h>
@@ -61,9 +63,9 @@ void GraphicBufferAllocator::dump(String8& result) const
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
- snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n",
+ snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %2d | 0x%08x\n",
list.keyAt(i), rec.size/1024.0f,
- rec.w, rec.h, rec.format, rec.usage);
+ rec.w, rec.s, rec.h, rec.format, rec.usage);
result.append(buffer);
total += rec.size;
}
@@ -71,16 +73,13 @@ void GraphicBufferAllocator::dump(String8& result) const
result.append(buffer);
}
-static inline uint32_t clamp(uint32_t c) {
- return c>0 ? c : 1;
-}
-
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
- // make sure to not allocate a 0 x 0 buffer
- w = clamp(w);
- h = clamp(h);
+ // make sure to not allocate a N x 0 or 0 x N buffer, since this is
+ // allowed from an API stand-point allocate a 1x1 buffer instead.
+ if (!w || !h)
+ w = h = 1;
// we have a h/w allocator and h/w buffer is requested
status_t err;
@@ -100,9 +99,9 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
alloc_rec_t rec;
rec.w = w;
rec.h = h;
+ rec.s = *stride;
rec.format = format;
rec.usage = usage;
- rec.vaddr = 0;
rec.size = h * stride[0] * bytesPerPixel(format);
list.add(*handle, rec);
} else {
diff --git a/libs/ui/GraphicLog.cpp b/libs/ui/GraphicLog.cpp
new file mode 100644
index 0000000..7ba2779
--- /dev/null
+++ b/libs/ui/GraphicLog.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 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 <unistd.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <utils/Endian.h>
+#include <utils/Timers.h>
+
+#include <ui/GraphicLog.h>
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(GraphicLog)
+
+static inline
+void writeInt32(uint8_t* base, size_t& pos, int32_t value) {
+#ifdef HAVE_LITTLE_ENDIAN
+ int32_t v = value;
+#else
+ int32_t v = htole32(value);
+#endif
+ base[pos] = EVENT_TYPE_INT;
+ memcpy(&base[pos+1], &v, sizeof(int32_t));
+ pos += 1+sizeof(int32_t);
+}
+
+static inline
+void writeInt64(uint8_t* base, size_t& pos, int64_t value) {
+#ifdef HAVE_LITTLE_ENDIAN
+ int64_t v = value;
+#else
+ int64_t v = htole64(value);
+#endif
+ base[pos] = EVENT_TYPE_LONG;
+ memcpy(&base[pos+1], &v, sizeof(int64_t));
+ pos += 1+sizeof(int64_t);
+}
+
+void GraphicLog::logImpl(int32_t tag, int32_t buffer)
+{
+ uint8_t scratch[2 + 2 + sizeof(int32_t) + sizeof(int64_t)];
+ size_t pos = 0;
+ scratch[pos++] = EVENT_TYPE_LIST;
+ scratch[pos++] = 2;
+ writeInt32(scratch, pos, buffer);
+ writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) ));
+ android_bWriteLog(tag, scratch, sizeof(scratch));
+}
+
+void GraphicLog::logImpl(int32_t tag, int32_t identity, int32_t buffer)
+{
+ uint8_t scratch[2 + 3 + sizeof(int32_t) + sizeof(int32_t) + sizeof(int64_t)];
+ size_t pos = 0;
+ scratch[pos++] = EVENT_TYPE_LIST;
+ scratch[pos++] = 3;
+ writeInt32(scratch, pos, buffer);
+ writeInt32(scratch, pos, identity);
+ writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) ));
+ android_bWriteLog(tag, scratch, sizeof(scratch));
+}
+
+GraphicLog::GraphicLog()
+ : mEnabled(0)
+{
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get("debug.graphic_log", property, NULL) > 0) {
+ mEnabled = atoi(property);
+ }
+}
+
+void GraphicLog::setEnabled(bool enable)
+{
+ mEnabled = enable;
+}
+
+}
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
new file mode 100644
index 0000000..811edaf
--- /dev/null
+++ b/libs/ui/Input.cpp
@@ -0,0 +1,215 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Provides a pipe-based transport for native events in the NDK.
+//
+#define LOG_TAG "Input"
+
+//#define LOG_NDEBUG 0
+
+#include <ui/Input.h>
+
+namespace android {
+
+// class InputEvent
+
+void InputEvent::initialize(int32_t deviceId, int32_t source) {
+ mDeviceId = deviceId;
+ mSource = source;
+}
+
+void InputEvent::initialize(const InputEvent& from) {
+ mDeviceId = from.mDeviceId;
+ mSource = from.mSource;
+}
+
+// class KeyEvent
+
+bool KeyEvent::hasDefaultAction(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_POWER:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MENU:
+ case AKEYCODE_NOTIFICATION:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MUTE:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::hasDefaultAction() const {
+ return hasDefaultAction(getKeyCode());
+}
+
+bool KeyEvent::isSystemKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MENU:
+ case AKEYCODE_SOFT_RIGHT:
+ case AKEYCODE_HOME:
+ case AKEYCODE_BACK:
+ case AKEYCODE_CALL:
+ case AKEYCODE_ENDCALL:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_POWER:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_CAMERA:
+ case AKEYCODE_FOCUS:
+ case AKEYCODE_SEARCH:
+ return true;
+ }
+
+ return false;
+}
+
+bool KeyEvent::isSystemKey() const {
+ return isSystemKey(getKeyCode());
+}
+
+void KeyEvent::initialize(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t keyCode,
+ int32_t scanCode,
+ int32_t metaState,
+ int32_t repeatCount,
+ nsecs_t downTime,
+ nsecs_t eventTime) {
+ InputEvent::initialize(deviceId, source);
+ mAction = action;
+ mFlags = flags;
+ mKeyCode = keyCode;
+ mScanCode = scanCode;
+ mMetaState = metaState;
+ mRepeatCount = repeatCount;
+ mDownTime = downTime;
+ mEventTime = eventTime;
+}
+
+void KeyEvent::initialize(const KeyEvent& from) {
+ InputEvent::initialize(from);
+ mAction = from.mAction;
+ mFlags = from.mFlags;
+ mKeyCode = from.mKeyCode;
+ mScanCode = from.mScanCode;
+ mMetaState = from.mMetaState;
+ mRepeatCount = from.mRepeatCount;
+ mDownTime = from.mDownTime;
+ mEventTime = from.mEventTime;
+}
+
+// class MotionEvent
+
+void MotionEvent::initialize(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t edgeFlags,
+ int32_t metaState,
+ float xOffset,
+ float yOffset,
+ float xPrecision,
+ float yPrecision,
+ nsecs_t downTime,
+ nsecs_t eventTime,
+ size_t pointerCount,
+ const int32_t* pointerIds,
+ const PointerCoords* pointerCoords) {
+ InputEvent::initialize(deviceId, source);
+ mAction = action;
+ mFlags = flags;
+ mEdgeFlags = edgeFlags;
+ mMetaState = metaState;
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+ mXPrecision = xPrecision;
+ mYPrecision = yPrecision;
+ mDownTime = downTime;
+ mPointerIds.clear();
+ mPointerIds.appendArray(pointerIds, pointerCount);
+ mSampleEventTimes.clear();
+ mSamplePointerCoords.clear();
+ addSample(eventTime, pointerCoords);
+}
+
+void MotionEvent::addSample(
+ int64_t eventTime,
+ const PointerCoords* pointerCoords) {
+ mSampleEventTimes.push(eventTime);
+ mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
+}
+
+void MotionEvent::offsetLocation(float xOffset, float yOffset) {
+ mXOffset += xOffset;
+ mYOffset += yOffset;
+}
+
+// class InputDeviceInfo
+
+InputDeviceInfo::InputDeviceInfo() {
+ initialize(-1, String8("uninitialized device info"));
+}
+
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
+ mId(other.mId), mName(other.mName), mSources(other.mSources),
+ mKeyboardType(other.mKeyboardType),
+ mMotionRanges(other.mMotionRanges) {
+}
+
+InputDeviceInfo::~InputDeviceInfo() {
+}
+
+void InputDeviceInfo::initialize(int32_t id, const String8& name) {
+ mId = id;
+ mName = name;
+ mSources = 0;
+ mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ mMotionRanges.clear();
+}
+
+const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const {
+ ssize_t index = mMotionRanges.indexOfKey(rangeType);
+ return index >= 0 ? & mMotionRanges.valueAt(index) : NULL;
+}
+
+void InputDeviceInfo::addSource(uint32_t source) {
+ mSources |= source;
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max,
+ float flat, float fuzz) {
+ MotionRange range = { min, max, flat, fuzz };
+ addMotionRange(rangeType, range);
+}
+
+void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) {
+ mMotionRanges.add(rangeType, range);
+}
+
+} // namespace android
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
new file mode 100644
index 0000000..28ccc43
--- /dev/null
+++ b/libs/ui/InputDispatcher.cpp
@@ -0,0 +1,3504 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input dispatcher.
+//
+#define LOG_TAG "InputDispatcher"
+
+//#define LOG_NDEBUG 0
+
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+#define DEBUG_INBOUND_EVENT_DETAILS 0
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+#define DEBUG_OUTBOUND_EVENT_DETAILS 0
+
+// Log debug messages about batching.
+#define DEBUG_BATCHING 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+// Log debug messages about registrations.
+#define DEBUG_REGISTRATION 0
+
+// Log debug messages about performance statistics.
+#define DEBUG_PERFORMANCE_STATISTICS 0
+
+// Log debug messages about input event injection.
+#define DEBUG_INJECTION 0
+
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
+// Log debug messages about input focus tracking.
+#define DEBUG_FOCUS 0
+
+// Log debug messages about the app switch latency optimization.
+#define DEBUG_APP_SWITCH 0
+
+#include <cutils/log.h>
+#include <ui/InputDispatcher.h>
+#include <ui/PowerManager.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+#define INDENT " "
+#define INDENT2 " "
+
+namespace android {
+
+// Delay before reporting long touch events to the power manager.
+const nsecs_t LONG_TOUCH_DELAY = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Amount of time to allow for all pending events to be processed when an app switch
+// key is on the way. This is used to preempt input dispatch and drop input events
+// when an application takes too long to respond and the user has pressed an app switch key.
+const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec
+
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
+ >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+}
+
+static bool isValidKeyAction(int32_t action) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN:
+ case AKEY_EVENT_ACTION_UP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool validateKeyEvent(int32_t action) {
+ if (! isValidKeyAction(action)) {
+ LOGE("Key event has invalid action code 0x%x", action);
+ return false;
+ }
+ return true;
+}
+
+static bool isValidMotionAction(int32_t action, size_t pointerCount) {
+ switch (action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_MOVE:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return true;
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ int32_t index = getMotionEventActionPointerIndex(action);
+ return index >= 0 && size_t(index) < pointerCount;
+ }
+ default:
+ return false;
+ }
+}
+
+static bool validateMotionEvent(int32_t action, size_t pointerCount,
+ const int32_t* pointerIds) {
+ if (! isValidMotionAction(action, pointerCount)) {
+ LOGE("Motion event has invalid action code 0x%x", action);
+ return false;
+ }
+ if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
+ LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+ pointerCount, MAX_POINTERS);
+ return false;
+ }
+ BitSet32 pointerIdBits;
+ for (size_t i = 0; i < pointerCount; i++) {
+ int32_t id = pointerIds[i];
+ if (id < 0 || id > MAX_POINTER_ID) {
+ LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
+ id, MAX_POINTER_ID);
+ return false;
+ }
+ if (pointerIdBits.hasBit(id)) {
+ LOGE("Motion event has duplicate pointer id %d", id);
+ return false;
+ }
+ pointerIdBits.markBit(id);
+ }
+ return true;
+}
+
+
+// --- InputWindow ---
+
+bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const {
+ return x >= touchableAreaLeft && x <= touchableAreaRight
+ && y >= touchableAreaTop && y <= touchableAreaBottom;
+}
+
+bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const {
+ return x >= frameLeft && x <= frameRight
+ && y >= frameTop && y <= frameBottom;
+}
+
+bool InputWindow::isTrustedOverlay() const {
+ return layoutParamsType == TYPE_INPUT_METHOD
+ || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
+ || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY;
+}
+
+
+// --- InputDispatcher ---
+
+InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
+ mPolicy(policy),
+ mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX),
+ mDispatchEnabled(true), mDispatchFrozen(false),
+ mFocusedWindow(NULL),
+ mFocusedApplication(NULL),
+ mCurrentInputTargetsValid(false),
+ mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+ mLooper = new Looper(false);
+
+ mInboundQueue.headSentinel.refCount = -1;
+ mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN;
+
+ mInboundQueue.tailSentinel.refCount = -1;
+ mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL;
+ mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX;
+
+ mKeyRepeatState.lastKeyEntry = NULL;
+
+ int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+ mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+ mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+ mThrottleState.originalSampleCount = 0;
+ LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+}
+
+InputDispatcher::~InputDispatcher() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ resetKeyRepeatLocked();
+ releasePendingEventLocked();
+ drainInboundQueueLocked();
+ }
+
+ while (mConnectionsByReceiveFd.size() != 0) {
+ unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel);
+ }
+}
+
+void InputDispatcher::dispatchOnce() {
+ nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
+ nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
+
+ nsecs_t nextWakeupTime = LONG_LONG_MAX;
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime);
+
+ if (runCommandsLockedInterruptible()) {
+ nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+ } // release lock
+
+ // Wait for callback or timeout or wake. (make sure we round up, not down)
+ nsecs_t currentTime = now();
+ int32_t timeoutMillis;
+ if (nextWakeupTime > currentTime) {
+ uint64_t timeout = uint64_t(nextWakeupTime - currentTime);
+ timeout = (timeout + 999999LL) / 1000000LL;
+ timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout);
+ } else {
+ timeoutMillis = 0;
+ }
+
+ mLooper->pollOnce(timeoutMillis);
+}
+
+void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
+ nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) {
+ nsecs_t currentTime = now();
+
+ // Reset the key repeat timer whenever we disallow key events, even if the next event
+ // is not a key. This is to ensure that we abort a key repeat if the device is just coming
+ // out of sleep.
+ if (keyRepeatTimeout < 0) {
+ resetKeyRepeatLocked();
+ }
+
+ // If dispatching is frozen, do not process timeouts or try to deliver any new events.
+ if (mDispatchFrozen) {
+#if DEBUG_FOCUS
+ LOGD("Dispatch frozen. Waiting some more.");
+#endif
+ return;
+ }
+
+ // Optimize latency of app switches.
+ // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
+ // been pressed. When it expires, we preempt dispatch and drop all other pending events.
+ bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
+ if (mAppSwitchDueTime < *nextWakeupTime) {
+ *nextWakeupTime = mAppSwitchDueTime;
+ }
+
+ // Ready to start a new event.
+ // If we don't already have a pending event, go grab one.
+ if (! mPendingEvent) {
+ if (mInboundQueue.isEmpty()) {
+ if (isAppSwitchDue) {
+ // The inbound queue is empty so the app switch key we were waiting
+ // for will never arrive. Stop waiting for it.
+ resetPendingAppSwitchLocked(false);
+ isAppSwitchDue = false;
+ }
+
+ // Synthesize a key repeat if appropriate.
+ if (mKeyRepeatState.lastKeyEntry) {
+ if (currentTime >= mKeyRepeatState.nextRepeatTime) {
+ mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay);
+ } else {
+ if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
+ *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
+ }
+ }
+ }
+ if (! mPendingEvent) {
+ return;
+ }
+ } else {
+ // Inbound queue has at least one entry.
+ EventEntry* entry = mInboundQueue.headSentinel.next;
+
+ // Throttle the entry if it is a move event and there are no
+ // other events behind it in the queue. Due to movement batching, additional
+ // samples may be appended to this event by the time the throttling timeout
+ // expires.
+ // TODO Make this smarter and consider throttling per device independently.
+ if (entry->type == EventEntry::TYPE_MOTION
+ && !isAppSwitchDue
+ && mDispatchEnabled
+ && (entry->policyFlags & POLICY_FLAG_PASS_TO_USER)
+ && !entry->isInjected()) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ int32_t deviceId = motionEntry->deviceId;
+ uint32_t source = motionEntry->source;
+ if (! isAppSwitchDue
+ && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event
+ && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+ && deviceId == mThrottleState.lastDeviceId
+ && source == mThrottleState.lastSource) {
+ nsecs_t nextTime = mThrottleState.lastEventTime
+ + mThrottleState.minTimeBetweenEvents;
+ if (currentTime < nextTime) {
+ // Throttle it!
+#if DEBUG_THROTTLING
+ LOGD("Throttling - Delaying motion event for "
+ "device 0x%x, source 0x%08x by up to %0.3fms.",
+ deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+ if (nextTime < *nextWakeupTime) {
+ *nextWakeupTime = nextTime;
+ }
+ if (mThrottleState.originalSampleCount == 0) {
+ mThrottleState.originalSampleCount =
+ motionEntry->countSamples();
+ }
+ return;
+ }
+ }
+
+#if DEBUG_THROTTLING
+ if (mThrottleState.originalSampleCount != 0) {
+ uint32_t count = motionEntry->countSamples();
+ LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+ count - mThrottleState.originalSampleCount,
+ mThrottleState.originalSampleCount, count);
+ mThrottleState.originalSampleCount = 0;
+ }
+#endif
+
+ mThrottleState.lastEventTime = entry->eventTime < currentTime
+ ? entry->eventTime : currentTime;
+ mThrottleState.lastDeviceId = deviceId;
+ mThrottleState.lastSource = source;
+ }
+
+ mInboundQueue.dequeue(entry);
+ mPendingEvent = entry;
+ }
+
+ // Poke user activity for this event.
+ if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ pokeUserActivityLocked(mPendingEvent);
+ }
+ }
+
+ // Now we have an event to dispatch.
+ assert(mPendingEvent != NULL);
+ bool done = false;
+ DropReason dropReason = DROP_REASON_NOT_DROPPED;
+ if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
+ dropReason = DROP_REASON_POLICY;
+ } else if (!mDispatchEnabled) {
+ dropReason = DROP_REASON_DISABLED;
+ }
+ switch (mPendingEvent->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ ConfigurationChangedEntry* typedEntry =
+ static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
+ dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
+ break;
+ }
+
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ if (isAppSwitchDue) {
+ if (isAppSwitchKeyEventLocked(typedEntry)) {
+ resetPendingAppSwitchLocked(true);
+ isAppSwitchDue = false;
+ } else if (dropReason == DROP_REASON_NOT_DROPPED) {
+ dropReason = DROP_REASON_APP_SWITCH;
+ }
+ }
+ done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout,
+ &dropReason, nextWakeupTime);
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
+ dropReason = DROP_REASON_APP_SWITCH;
+ }
+ done = dispatchMotionLocked(currentTime, typedEntry,
+ &dropReason, nextWakeupTime);
+ break;
+ }
+
+ default:
+ assert(false);
+ break;
+ }
+
+ if (done) {
+ if (dropReason != DROP_REASON_NOT_DROPPED) {
+ dropInboundEventLocked(mPendingEvent, dropReason);
+ }
+
+ releasePendingEventLocked();
+ *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
+ }
+}
+
+bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+ bool needWake = mInboundQueue.isEmpty();
+ mInboundQueue.enqueueAtTail(entry);
+
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+ if (isAppSwitchKeyEventLocked(keyEntry)) {
+ if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+ mAppSwitchSawKeyDown = true;
+ } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ if (mAppSwitchSawKeyDown) {
+#if DEBUG_APP_SWITCH
+ LOGD("App switch is pending!");
+#endif
+ mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+ mAppSwitchSawKeyDown = false;
+ needWake = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return needWake;
+}
+
+void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
+ const char* reason;
+ switch (dropReason) {
+ case DROP_REASON_POLICY:
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("Dropped event because policy consumed it.");
+#endif
+ reason = "inbound event was dropped because the policy consumed it";
+ break;
+ case DROP_REASON_DISABLED:
+ LOGI("Dropped event because input dispatch is disabled.");
+ reason = "inbound event was dropped because input dispatch is disabled";
+ break;
+ case DROP_REASON_APP_SWITCH:
+ LOGI("Dropped event because of pending overdue app switch.");
+ reason = "inbound event was dropped because of pending overdue app switch";
+ break;
+ default:
+ assert(false);
+ return;
+ }
+
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ synthesizeCancelationEventsForAllConnectionsLocked(
+ InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ break;
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ synthesizeCancelationEventsForAllConnectionsLocked(
+ InputState::CANCEL_POINTER_EVENTS, reason);
+ } else {
+ synthesizeCancelationEventsForAllConnectionsLocked(
+ InputState::CANCEL_NON_POINTER_EVENTS, reason);
+ }
+ break;
+ }
+ }
+}
+
+bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) {
+ return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+}
+
+bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
+ return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED)
+ && isAppSwitchKeyCode(keyEntry->keyCode)
+ && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED)
+ && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+}
+
+bool InputDispatcher::isAppSwitchPendingLocked() {
+ return mAppSwitchDueTime != LONG_LONG_MAX;
+}
+
+void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
+ mAppSwitchDueTime = LONG_LONG_MAX;
+
+#if DEBUG_APP_SWITCH
+ if (handled) {
+ LOGD("App switch has arrived.");
+ } else {
+ LOGD("App switch was abandoned.");
+ }
+#endif
+}
+
+bool InputDispatcher::runCommandsLockedInterruptible() {
+ if (mCommandQueue.isEmpty()) {
+ return false;
+ }
+
+ do {
+ CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
+
+ Command command = commandEntry->command;
+ (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+
+ commandEntry->connection.clear();
+ mAllocator.releaseCommandEntry(commandEntry);
+ } while (! mCommandQueue.isEmpty());
+ return true;
+}
+
+InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
+ CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command);
+ mCommandQueue.enqueueAtTail(commandEntry);
+ return commandEntry;
+}
+
+void InputDispatcher::drainInboundQueueLocked() {
+ while (! mInboundQueue.isEmpty()) {
+ EventEntry* entry = mInboundQueue.dequeueAtHead();
+ releaseInboundEventLocked(entry);
+ }
+}
+
+void InputDispatcher::releasePendingEventLocked() {
+ if (mPendingEvent) {
+ releaseInboundEventLocked(mPendingEvent);
+ mPendingEvent = NULL;
+ }
+}
+
+void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("Injected inbound event was dropped.");
+#endif
+ setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ }
+ mAllocator.releaseEventEntry(entry);
+}
+
+void InputDispatcher::resetKeyRepeatLocked() {
+ if (mKeyRepeatState.lastKeyEntry) {
+ mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
+ mKeyRepeatState.lastKeyEntry = NULL;
+ }
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(
+ nsecs_t currentTime, nsecs_t keyRepeatDelay) {
+ KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+ // Reuse the repeated key entry if it is otherwise unreferenced.
+ uint32_t policyFlags = (entry->policyFlags & POLICY_FLAG_RAW_MASK)
+ | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED;
+ if (entry->refCount == 1) {
+ mAllocator.recycleKeyEntry(entry);
+ entry->eventTime = currentTime;
+ entry->policyFlags = policyFlags;
+ entry->repeatCount += 1;
+ } else {
+ KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
+ entry->deviceId, entry->source, policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode,
+ entry->metaState, entry->repeatCount + 1, entry->downTime);
+
+ mKeyRepeatState.lastKeyEntry = newEntry;
+ mAllocator.releaseKeyEntry(entry);
+
+ entry = newEntry;
+ }
+ entry->syntheticRepeat = true;
+
+ // Increment reference count since we keep a reference to the event in
+ // mKeyRepeatState.lastKeyEntry in addition to the one we return.
+ entry->refCount += 1;
+
+ if (entry->repeatCount == 1) {
+ entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
+ }
+
+ mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay;
+ return entry;
+}
+
+bool InputDispatcher::dispatchConfigurationChangedLocked(
+ nsecs_t currentTime, ConfigurationChangedEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime);
+#endif
+
+ // Reset key repeating in case a keyboard device was added or removed or something.
+ resetKeyRepeatLocked();
+
+ // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyConfigurationChangedInterruptible);
+ commandEntry->eventTime = entry->eventTime;
+ return true;
+}
+
+bool InputDispatcher::dispatchKeyLocked(
+ nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ if (entry->repeatCount == 0
+ && entry->action == AKEY_EVENT_ACTION_DOWN
+ && (entry->policyFlags & POLICY_FLAG_TRUSTED)
+ && !entry->isInjected()) {
+ if (mKeyRepeatState.lastKeyEntry
+ && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+ // We have seen two identical key downs in a row which indicates that the device
+ // driver is automatically generating key repeats itself. We take note of the
+ // repeat here, but we disable our own next key repeat timer since it is clear that
+ // we will not need to synthesize key repeats ourselves.
+ entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+ } else {
+ // Not a repeat. Save key down state in case we do see a repeat later.
+ resetKeyRepeatLocked();
+ mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+ }
+ mKeyRepeatState.lastKeyEntry = entry;
+ entry->refCount += 1;
+ } else if (! entry->syntheticRepeat) {
+ resetKeyRepeatLocked();
+ }
+
+ entry->dispatchInProgress = true;
+ resetTargetsLocked();
+
+ logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+ }
+
+ // Give the policy a chance to intercept the key.
+ if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
+ if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
+ if (mFocusedWindow) {
+ commandEntry->inputChannel = mFocusedWindow->inputChannel;
+ }
+ commandEntry->keyEntry = entry;
+ entry->refCount += 1;
+ return false; // wait for the command to run
+ } else {
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ }
+ } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
+ if (*dropReason == DROP_REASON_NOT_DROPPED) {
+ *dropReason = DROP_REASON_POLICY;
+ }
+ }
+
+ // Clean up if dropping the event.
+ if (*dropReason != DROP_REASON_NOT_DROPPED) {
+ resetTargetsLocked();
+ setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+ return true;
+ }
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ commitTargetsLocked();
+ }
+
+ // Dispatch the key.
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ return true;
+}
+
+void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
+ "repeatCount=%d, downTime=%lld",
+ prefix,
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+ entry->repeatCount, entry->downTime);
+#endif
+}
+
+bool InputDispatcher::dispatchMotionLocked(
+ nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+ // Preprocessing.
+ if (! entry->dispatchInProgress) {
+ entry->dispatchInProgress = true;
+ resetTargetsLocked();
+
+ logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+ }
+
+ // Clean up if dropping the event.
+ if (*dropReason != DROP_REASON_NOT_DROPPED) {
+ resetTargetsLocked();
+ setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
+ return true;
+ }
+
+ bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+
+ // Identify targets.
+ if (! mCurrentInputTargetsValid) {
+ int32_t injectionResult;
+ if (isPointerEvent) {
+ // Pointer event. (eg. touchscreen)
+ injectionResult = findTouchedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
+ } else {
+ // Non touch event. (eg. trackball)
+ injectionResult = findFocusedWindowTargetsLocked(currentTime,
+ entry, nextWakeupTime);
+ }
+ if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
+ return false;
+ }
+
+ setInjectionResultLocked(entry, injectionResult);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return true;
+ }
+
+ addMonitoringTargetsLocked();
+ commitTargetsLocked();
+ }
+
+ // Dispatch the motion.
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ return true;
+}
+
+
+void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, "
+ "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld",
+ prefix,
+ entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
+ entry->action, entry->flags,
+ entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
+ entry->downTime);
+
+ // Print the most recent sample that we have available, this may change due to batching.
+ size_t sampleCount = 1;
+ const MotionSample* sample = & entry->firstSample;
+ for (; sample->next != NULL; sample = sample->next) {
+ sampleCount += 1;
+ }
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, entry->pointerIds[i],
+ sample->pointerCoords[i].x, sample->pointerCoords[i].y,
+ sample->pointerCoords[i].pressure, sample->pointerCoords[i].size,
+ sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor,
+ sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor,
+ sample->pointerCoords[i].orientation);
+ }
+
+ // Keep in mind that due to batching, it is possible for the number of samples actually
+ // dispatched to change before the application finally consumed them.
+ if (entry->action == AMOTION_EVENT_ACTION_MOVE) {
+ LOGD(" ... Total movement samples currently batched %d ...", sampleCount);
+ }
+#endif
+}
+
+void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
+ EventEntry* eventEntry, bool resumeWithAppendedMotionSample) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("dispatchEventToCurrentInputTargets - "
+ "resumeWithAppendedMotionSample=%s",
+ toString(resumeWithAppendedMotionSample));
+#endif
+
+ assert(eventEntry->dispatchInProgress); // should already have been set to true
+
+ pokeUserActivityLocked(eventEntry);
+
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
+
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
+ resumeWithAppendedMotionSample);
+ } else {
+#if DEBUG_FOCUS
+ LOGD("Dropping event delivery to target with channel '%s' because it "
+ "is no longer registered with the input dispatcher.",
+ inputTarget.inputChannel->getName().string());
+#endif
+ }
+ }
+}
+
+void InputDispatcher::resetTargetsLocked() {
+ mCurrentInputTargetsValid = false;
+ mCurrentInputTargets.clear();
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+void InputDispatcher::commitTargetsLocked() {
+ mCurrentInputTargetsValid = true;
+}
+
+int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
+ const EventEntry* entry, const InputApplication* application, const InputWindow* window,
+ nsecs_t* nextWakeupTime) {
+ if (application == NULL && window == NULL) {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for system to become ready for input.");
+#endif
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ } else {
+ if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+#if DEBUG_FOCUS
+ LOGD("Waiting for application to become ready for input: %s",
+ getApplicationWindowLabelLocked(application, window).string());
+#endif
+ nsecs_t timeout = window ? window->dispatchingTimeout :
+ application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
+ mInputTargetWaitStartTime = currentTime;
+ mInputTargetWaitTimeoutTime = currentTime + timeout;
+ mInputTargetWaitTimeoutExpired = false;
+ }
+ }
+
+ if (mInputTargetWaitTimeoutExpired) {
+ return INPUT_EVENT_INJECTION_TIMED_OUT;
+ }
+
+ if (currentTime >= mInputTargetWaitTimeoutTime) {
+ onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime);
+
+ // Force poll loop to wake up immediately on next iteration once we get the
+ // ANR response back from the policy.
+ *nextWakeupTime = LONG_LONG_MIN;
+ return INPUT_EVENT_INJECTION_PENDING;
+ } else {
+ // Force poll loop to wake up when timeout is due.
+ if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
+ *nextWakeupTime = mInputTargetWaitTimeoutTime;
+ }
+ return INPUT_EVENT_INJECTION_PENDING;
+ }
+}
+
+void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
+ const sp<InputChannel>& inputChannel) {
+ if (newTimeout > 0) {
+ // Extend the timeout.
+ mInputTargetWaitTimeoutTime = now() + newTimeout;
+ } else {
+ // Give up.
+ mInputTargetWaitTimeoutExpired = true;
+
+ // Release the touch targets.
+ mTouchState.reset();
+
+ // Input state will not be realistic. Mark it out of sync.
+ if (inputChannel.get()) {
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ synthesizeCancelationEventsForConnectionLocked(
+ connection, InputState::CANCEL_ALL_EVENTS,
+ "application not responding");
+ }
+ }
+ }
+}
+
+nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(
+ nsecs_t currentTime) {
+ if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
+ return currentTime - mInputTargetWaitStartTime;
+ }
+ return 0;
+}
+
+void InputDispatcher::resetANRTimeoutsLocked() {
+#if DEBUG_FOCUS
+ LOGD("Resetting ANR timeouts.");
+#endif
+
+ // Reset input target wait timeout.
+ mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
+}
+
+int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
+ const EventEntry* entry, nsecs_t* nextWakeupTime) {
+ mCurrentInputTargets.clear();
+
+ int32_t injectionResult;
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may eventually add a window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, NULL, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // If the currently focused window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, mFocusedWindow, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // Success! Output targets.
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0));
+
+ // Done.
+Failed:
+Unresponsive:
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
+#if DEBUG_FOCUS
+ LOGD("findFocusedWindow finished: injectionResult=%d, "
+ "timeSpendWaitingForApplication=%0.1fms",
+ injectionResult, timeSpentWaitingForApplication / 1000000.0);
+#endif
+ return injectionResult;
+}
+
+int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
+ const MotionEntry* entry, nsecs_t* nextWakeupTime) {
+ enum InjectionPermission {
+ INJECTION_PERMISSION_UNKNOWN,
+ INJECTION_PERMISSION_GRANTED,
+ INJECTION_PERMISSION_DENIED
+ };
+
+ mCurrentInputTargets.clear();
+
+ nsecs_t startTime = now();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+
+ int32_t action = entry->action;
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+
+ // Update the touch state as needed based on the properties of the touch event.
+ int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ mTempTouchState.reset();
+ mTempTouchState.down = true;
+ } else {
+ mTempTouchState.copyFrom(mTouchState);
+ }
+
+ bool isSplit = mTempTouchState.split && mTempTouchState.down;
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+ || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
+ /* Case 1: New splittable pointer going down. */
+
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x);
+ int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y);
+ const InputWindow* newTouchedWindow = NULL;
+ const InputWindow* topErrorWindow = NULL;
+
+ // Traverse windows from front to back to find touched window and outside targets.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ const InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & InputWindow::FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE
+ | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+ if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) {
+ newTouchedWindow = window;
+ }
+ break; // found touched window, exit window loop
+ }
+ }
+
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN
+ && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) {
+ int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE;
+ if (isWindowObscuredAtPointLocked(window, x, y)) {
+ outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0));
+ }
+ }
+ }
+
+ // If there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because system error window is pending.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, NULL, nextWakeupTime);
+ injectionPermission = INJECTION_PERMISSION_UNKNOWN;
+ goto Unresponsive;
+ }
+
+ // Figure out whether splitting will be allowed for this window.
+ if (newTouchedWindow
+ && (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH)) {
+ // New window supports splitting.
+ isSplit = true;
+ } else if (isSplit) {
+ // New window does not support splitting but we have already split events.
+ // Assign the pointer to the first foreground window we find.
+ // (May be NULL which is why we put this code block before the next check.)
+ newTouchedWindow = mTempTouchState.getFirstForegroundWindow();
+ }
+
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no touched window but there is a "
+ "focused application that may eventually add a new window: %s.",
+ getApplicationWindowLabelLocked(mFocusedApplication, NULL).string());
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ mFocusedApplication, NULL, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ LOGI("Dropping event because there is no touched window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ // Set target flags.
+ int32_t targetFlags = InputTarget::FLAG_FOREGROUND;
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
+ }
+ if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ // Update the temporary touch state.
+ BitSet32 pointerIds;
+ if (isSplit) {
+ uint32_t pointerId = entry->pointerIds[pointerIndex];
+ pointerIds.markBit(pointerId);
+ }
+ mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds);
+ } else {
+ /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
+
+ // If the pointer is not currently down, then ignore the event.
+ if (! mTempTouchState.down) {
+ LOGI("Dropping event because the pointer is not down.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+ }
+
+ // Check permission to inject into all touched foreground windows and ensure there
+ // is at least one touched foreground window.
+ {
+ bool haveForegroundWindow = false;
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ haveForegroundWindow = true;
+ if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ goto Failed;
+ }
+ }
+ }
+ if (! haveForegroundWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched foreground window to receive it.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ goto Failed;
+ }
+
+ // Permission granted to injection into all touched foreground windows.
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ }
+
+ // Ensure all touched foreground windows are ready for new input.
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
+ // If the touched window is paused then keep waiting.
+ if (touchedWindow.window->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, touchedWindow.window, nextWakeupTime);
+ goto Unresponsive;
+ }
+
+ // If the touched window is still working on previous events then keep waiting.
+ if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because touched window still processing previous input.");
+#endif
+ injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
+ NULL, touchedWindow.window, nextWakeupTime);
+ goto Unresponsive;
+ }
+ }
+ }
+
+ // If this is the first pointer going down and the touched window has a wallpaper
+ // then also add the touched wallpaper windows so they are locked in for the duration
+ // of the touch gesture.
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+ if (foregroundWindow->hasWallpaper) {
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ const InputWindow* window = & mWindows[i];
+ if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) {
+ mTempTouchState.addOrUpdateWindow(window,
+ InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0));
+ }
+ }
+ }
+ }
+
+ // Success! Output targets.
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
+ addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags,
+ touchedWindow.pointerIds);
+ }
+
+ // Drop the outside touch window since we will not care about them in the next iteration.
+ mTempTouchState.removeOutsideTouchWindows();
+
+Failed:
+ // Check injection permission once and for all.
+ if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
+ if (checkInjectionPermission(NULL, entry->injectionState)) {
+ injectionPermission = INJECTION_PERMISSION_GRANTED;
+ } else {
+ injectionPermission = INJECTION_PERMISSION_DENIED;
+ }
+ }
+
+ // Update final pieces of touch state if the injector had permission.
+ if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
+ if (maskedAction == AMOTION_EVENT_ACTION_UP
+ || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // All pointers up or canceled.
+ mTempTouchState.reset();
+ } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ // First pointer went down.
+ if (mTouchState.down) {
+#if DEBUG_FOCUS
+ LOGD("Pointer down received while already down.");
+#endif
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ // One pointer went up.
+ if (isSplit) {
+ int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ uint32_t pointerId = entry->pointerIds[pointerIndex];
+
+ for (size_t i = 0; i < mTempTouchState.windows.size(); ) {
+ TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
+ if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+ touchedWindow.pointerIds.clearBit(pointerId);
+ if (touchedWindow.pointerIds.isEmpty()) {
+ mTempTouchState.windows.removeAt(i);
+ continue;
+ }
+ }
+ i += 1;
+ }
+ }
+ }
+
+ // Save changes to touch state.
+ mTouchState.copyFrom(mTempTouchState);
+ } else {
+#if DEBUG_FOCUS
+ LOGD("Not updating touch focus because injection was denied.");
+#endif
+ }
+
+Unresponsive:
+ // Reset temporary touch state to ensure we release unnecessary references to input channels.
+ mTempTouchState.reset();
+
+ nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
+ updateDispatchStatisticsLocked(currentTime, entry,
+ injectionResult, timeSpentWaitingForApplication);
+#if DEBUG_FOCUS
+ LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+ "timeSpentWaitingForApplication=%0.1fms",
+ injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
+#endif
+ return injectionResult;
+}
+
+void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags,
+ BitSet32 pointerIds) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+ target.pointerIds = pointerIds;
+}
+
+void InputDispatcher::addMonitoringTargetsLocked() {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ mCurrentInputTargets.push();
+
+ InputTarget& target = mCurrentInputTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
+bool InputDispatcher::checkInjectionPermission(const InputWindow* window,
+ const InjectionState* injectionState) {
+ if (injectionState
+ && (window == NULL || window->ownerUid != injectionState->injectorUid)
+ && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
+ if (window) {
+ LOGW("Permission denied: injecting event from pid %d uid %d to window "
+ "with input channel %s owned by uid %d",
+ injectionState->injectorPid, injectionState->injectorUid,
+ window->inputChannel->getName().string(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectionState->injectorPid, injectionState->injectorUid);
+ }
+ return false;
+ }
+ return true;
+}
+
+bool InputDispatcher::isWindowObscuredAtPointLocked(
+ const InputWindow* window, int32_t x, int32_t y) const {
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ const InputWindow* other = & mWindows.itemAt(i);
+ if (other == window) {
+ break;
+ }
+ if (other->visible && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) {
+ ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel);
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ return connection->outboundQueue.isEmpty();
+ } else {
+ return true;
+ }
+}
+
+String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application,
+ const InputWindow* window) {
+ if (application) {
+ if (window) {
+ String8 label(application->name);
+ label.append(" - ");
+ label.append(window->name);
+ return label;
+ } else {
+ return application->name;
+ }
+ } else if (window) {
+ return window->name;
+ } else {
+ return String8("<unknown application or window>");
+ }
+}
+
+void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
+ int32_t eventType = POWER_MANAGER_BUTTON_EVENT;
+ if (eventEntry->type == EventEntry::TYPE_MOTION) {
+ const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ switch (motionEntry->action) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ break;
+ case AMOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (motionEntry->eventTime - motionEntry->downTime < LONG_TOUCH_DELAY) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
+ }
+ break;
+ }
+ }
+ }
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doPokeUserActivityLockedInterruptible);
+ commandEntry->eventTime = eventEntry->eventTime;
+ commandEntry->userActivityEventType = eventType;
+}
+
+void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
+ bool resumeWithAppendedMotionSample) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
+ "xOffset=%f, yOffset=%f, "
+ "windowType=%d, pointerIds=0x%x, "
+ "resumeWithAppendedMotionSample=%s",
+ connection->getInputChannelName(), inputTarget->flags,
+ inputTarget->xOffset, inputTarget->yOffset,
+ inputTarget->windowType, inputTarget->pointerIds.value,
+ toString(resumeWithAppendedMotionSample));
+#endif
+
+ // Make sure we are never called for streaming when splitting across multiple windows.
+ bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
+ assert(! (resumeWithAppendedMotionSample && isSplit));
+
+ // Skip this event if the connection status is not normal.
+ // We don't want to enqueue additional outbound events if the connection is broken.
+ if (connection->status != Connection::STATUS_NORMAL) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Dropping event because the channel status is %s",
+ connection->getInputChannelName(), connection->getStatusLabel());
+#endif
+ return;
+ }
+
+ // Split a motion event if needed.
+ if (isSplit) {
+ assert(eventEntry->type == EventEntry::TYPE_MOTION);
+
+ MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
+ if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+ MotionEntry* splitMotionEntry = splitMotionEvent(
+ originalMotionEntry, inputTarget->pointerIds);
+#if DEBUG_FOCUS
+ LOGD("channel '%s' ~ Split motion event.",
+ connection->getInputChannelName());
+ logOutboundMotionDetailsLocked(" ", splitMotionEntry);
+#endif
+ eventEntry = splitMotionEntry;
+ }
+ }
+
+ // Resume the dispatch cycle with a freshly appended motion sample.
+ // First we check that the last dispatch entry in the outbound queue is for the same
+ // motion event to which we appended the motion sample. If we find such a dispatch
+ // entry, and if it is currently in progress then we try to stream the new sample.
+ bool wasEmpty = connection->outboundQueue.isEmpty();
+
+ if (! wasEmpty && resumeWithAppendedMotionSample) {
+ DispatchEntry* motionEventDispatchEntry =
+ connection->findQueuedDispatchEntryForEvent(eventEntry);
+ if (motionEventDispatchEntry) {
+ // If the dispatch entry is not in progress, then we must be busy dispatching an
+ // earlier event. Not a problem, the motion event is on the outbound queue and will
+ // be dispatched later.
+ if (! motionEventDispatchEntry->inProgress) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Not streaming because the motion event has "
+ "not yet been dispatched. "
+ "(Waiting for earlier events to be consumed.)",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+ // If the dispatch entry is in progress but it already has a tail of pending
+ // motion samples, then it must mean that the shared memory buffer filled up.
+ // Not a problem, when this dispatch cycle is finished, we will eventually start
+ // a new dispatch cycle to process the tail and that tail includes the newly
+ // appended motion sample.
+ if (motionEventDispatchEntry->tailMotionSample) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Not streaming because no new samples can "
+ "be appended to the motion event in this dispatch cycle. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+ // The dispatch entry is in progress and is still potentially open for streaming.
+ // Try to stream the new motion sample. This might fail if the consumer has already
+ // consumed the motion event (or if the channel is broken).
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ MotionSample* appendedMotionSample = motionEntry->lastSample;
+ status_t status = connection->inputPublisher.appendMotionSample(
+ appendedMotionSample->eventTime, appendedMotionSample->pointerCoords);
+ if (status == OK) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Successfully streamed new motion sample.",
+ connection->getInputChannelName());
+#endif
+ return;
+ }
+
+#if DEBUG_BATCHING
+ if (status == NO_MEMORY) {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event because the shared memory buffer is full. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+ } else if (status == status_t(FAILED_TRANSACTION)) {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event because the event has already been consumed. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName());
+ } else {
+ LOGD("channel '%s' ~ Could not append motion sample to currently "
+ "dispatched move event due to an error, status=%d. "
+ "(Waiting for next dispatch cycle to start.)",
+ connection->getInputChannelName(), status);
+ }
+#endif
+ // Failed to stream. Start a new tail of pending motion samples to dispatch
+ // in the next cycle.
+ motionEventDispatchEntry->tailMotionSample = appendedMotionSample;
+ return;
+ }
+ }
+
+ // This is a new event.
+ // Enqueue a new dispatch entry onto the outbound queue for this connection.
+ DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref
+ inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset);
+ if (dispatchEntry->hasForegroundTarget()) {
+ incrementPendingForegroundDispatchesLocked(eventEntry);
+ }
+
+ // Handle the case where we could not stream a new motion sample because the consumer has
+ // already consumed the motion event (otherwise the corresponding dispatch entry would
+ // still be in the outbound queue for this connection). We set the head motion sample
+ // to the list starting with the newly appended motion sample.
+ if (resumeWithAppendedMotionSample) {
+#if DEBUG_BATCHING
+ LOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples "
+ "that cannot be streamed because the motion event has already been consumed.",
+ connection->getInputChannelName());
+#endif
+ MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample;
+ dispatchEntry->headMotionSample = appendedMotionSample;
+ }
+
+ // Enqueue the dispatch entry.
+ connection->outboundQueue.enqueueAtTail(dispatchEntry);
+
+ // If the outbound queue was previously empty, start the dispatch cycle going.
+ if (wasEmpty) {
+ activateConnectionLocked(connection.get());
+ startDispatchCycleLocked(currentTime, connection);
+ }
+}
+
+void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ startDispatchCycle",
+ connection->getInputChannelName());
+#endif
+
+ assert(connection->status == Connection::STATUS_NORMAL);
+ assert(! connection->outboundQueue.isEmpty());
+
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ assert(! dispatchEntry->inProgress);
+
+ // Mark the dispatch entry as in progress.
+ dispatchEntry->inProgress = true;
+
+ // Update the connection's input state.
+ EventEntry* eventEntry = dispatchEntry->eventEntry;
+ InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry);
+
+#if FILTER_INPUT_EVENTS
+ // Filter out inconsistent sequences of input events.
+ // The input system may drop or inject events in a way that could violate implicit
+ // invariants on input state and potentially cause an application to crash
+ // or think that a key or pointer is stuck down. Technically we make no guarantees
+ // of consistency but it would be nice to improve on this where possible.
+ // XXX: This code is a proof of concept only. Not ready for prime time.
+ if (consistency == InputState::TOLERABLE) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's "
+ "current input state but that is likely to be tolerated by the application.",
+ connection->getInputChannelName());
+#endif
+ } else if (consistency == InputState::BROKEN) {
+ LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's "
+ "current input state and that is likely to cause the application to crash.",
+ connection->getInputChannelName());
+ startNextDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+#endif
+
+ // Publish the event.
+ status_t status;
+ switch (eventEntry->type) {
+ case EventEntry::TYPE_KEY: {
+ KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+
+ // Apply target flags.
+ int32_t action = keyEntry->action;
+ int32_t flags = keyEntry->flags;
+
+ // Publish the key event.
+ status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
+ action, flags, keyEntry->keyCode, keyEntry->scanCode,
+ keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
+ keyEntry->eventTime);
+
+ if (status) {
+ LOGE("channel '%s' ~ Could not publish key event, "
+ "status=%d", connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+ break;
+ }
+
+ case EventEntry::TYPE_MOTION: {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+
+ // Apply target flags.
+ int32_t action = motionEntry->action;
+ int32_t flags = motionEntry->flags;
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) {
+ action = AMOTION_EVENT_ACTION_OUTSIDE;
+ }
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+ flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ }
+
+ // If headMotionSample is non-NULL, then it points to the first new sample that we
+ // were unable to dispatch during the previous cycle so we resume dispatching from
+ // that point in the list of motion samples.
+ // Otherwise, we just start from the first sample of the motion event.
+ MotionSample* firstMotionSample = dispatchEntry->headMotionSample;
+ if (! firstMotionSample) {
+ firstMotionSample = & motionEntry->firstSample;
+ }
+
+ // Set the X and Y offset depending on the input source.
+ float xOffset, yOffset;
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ xOffset = dispatchEntry->xOffset;
+ yOffset = dispatchEntry->yOffset;
+ } else {
+ xOffset = 0.0f;
+ yOffset = 0.0f;
+ }
+
+ // Publish the motion event and the first motion sample.
+ status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
+ motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState,
+ xOffset, yOffset,
+ motionEntry->xPrecision, motionEntry->yPrecision,
+ motionEntry->downTime, firstMotionSample->eventTime,
+ motionEntry->pointerCount, motionEntry->pointerIds,
+ firstMotionSample->pointerCoords);
+
+ if (status) {
+ LOGE("channel '%s' ~ Could not publish motion event, "
+ "status=%d", connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+
+ // Append additional motion samples.
+ MotionSample* nextMotionSample = firstMotionSample->next;
+ for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
+ status = connection->inputPublisher.appendMotionSample(
+ nextMotionSample->eventTime, nextMotionSample->pointerCoords);
+ if (status == NO_MEMORY) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will "
+ "be sent in the next dispatch cycle.",
+ connection->getInputChannelName());
+#endif
+ break;
+ }
+ if (status != OK) {
+ LOGE("channel '%s' ~ Could not append motion sample "
+ "for a reason other than out of memory, status=%d",
+ connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+ }
+
+ // Remember the next motion sample that we could not dispatch, in case we ran out
+ // of space in the shared memory buffer.
+ dispatchEntry->tailMotionSample = nextMotionSample;
+ break;
+ }
+
+ default: {
+ assert(false);
+ }
+ }
+
+ // Send the dispatch signal.
+ status = connection->inputPublisher.sendDispatchSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Could not send dispatch signal, status=%d",
+ connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+
+ // Record information about the newly started dispatch cycle.
+ connection->lastEventTime = eventEntry->eventTime;
+ connection->lastDispatchTime = currentTime;
+
+ // Notify other system components.
+ onDispatchCycleStartedLocked(currentTime, connection);
+}
+
+void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
+ "%01.1fms since dispatch",
+ connection->getInputChannelName(),
+ connection->getEventLatencyMillis(currentTime),
+ connection->getDispatchLatencyMillis(currentTime));
+#endif
+
+ if (connection->status == Connection::STATUS_BROKEN
+ || connection->status == Connection::STATUS_ZOMBIE) {
+ return;
+ }
+
+ // Notify other system components.
+ onDispatchCycleFinishedLocked(currentTime, connection);
+
+ // Reset the publisher since the event has been consumed.
+ // We do this now so that the publisher can release some of its internal resources
+ // while waiting for the next dispatch cycle to begin.
+ status_t status = connection->inputPublisher.reset();
+ if (status) {
+ LOGE("channel '%s' ~ Could not reset publisher, status=%d",
+ connection->getInputChannelName(), status);
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+
+ startNextDispatchCycleLocked(currentTime, connection);
+}
+
+void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+ // Start the next dispatch cycle for this connection.
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (dispatchEntry->inProgress) {
+ // Finish or resume current event in progress.
+ if (dispatchEntry->tailMotionSample) {
+ // We have a tail of undispatched motion samples.
+ // Reuse the same DispatchEntry and start a new cycle.
+ dispatchEntry->inProgress = false;
+ dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample;
+ dispatchEntry->tailMotionSample = NULL;
+ startDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+ // Finished.
+ connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+ } else {
+ // If the head is not in progress, then we must have already dequeued the in
+ // progress event, which means we actually aborted it.
+ // So just start the next event for this connection.
+ startDispatchCycleLocked(currentTime, connection);
+ return;
+ }
+ }
+
+ // Outbound queue is empty, deactivate the connection.
+ deactivateConnectionLocked(connection.get());
+}
+
+void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
+ const sp<Connection>& connection) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ abortBrokenDispatchCycle - broken=%s",
+ connection->getInputChannelName(), toString(broken));
+#endif
+
+ // Clear the outbound queue.
+ drainOutboundQueueLocked(connection.get());
+
+ // The connection appears to be unrecoverably broken.
+ // Ignore already broken or zombie connections.
+ if (connection->status == Connection::STATUS_NORMAL) {
+ connection->status = Connection::STATUS_BROKEN;
+
+ // Notify other system components.
+ onDispatchCycleBrokenLocked(currentTime, connection);
+ }
+}
+
+void InputDispatcher::drainOutboundQueueLocked(Connection* connection) {
+ while (! connection->outboundQueue.isEmpty()) {
+ DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
+ if (dispatchEntry->hasForegroundTarget()) {
+ decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+ }
+ mAllocator.releaseDispatchEntry(dispatchEntry);
+ }
+
+ deactivateConnectionLocked(connection);
+}
+
+int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
+ InputDispatcher* d = static_cast<InputDispatcher*>(data);
+
+ { // acquire lock
+ AutoMutex _l(d->mLock);
+
+ ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ if (connectionIndex < 0) {
+ LOGE("Received spurious receive callback for unknown input channel. "
+ "fd=%d, events=0x%x", receiveFd, events);
+ return 0; // remove the callback
+ }
+
+ nsecs_t currentTime = now();
+
+ sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+ LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ d->abortBrokenDispatchCycleLocked(currentTime, connection);
+ d->runCommandsLockedInterruptible();
+ return 0; // remove the callback
+ }
+
+ if (! (events & ALOOPER_EVENT_INPUT)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ return 1;
+ }
+
+ status_t status = connection->inputPublisher.receiveFinishedSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
+ connection->getInputChannelName(), status);
+ d->abortBrokenDispatchCycleLocked(currentTime, connection);
+ d->runCommandsLockedInterruptible();
+ return 0; // remove the callback
+ }
+
+ d->finishDispatchCycleLocked(currentTime, connection);
+ d->runCommandsLockedInterruptible();
+ return 1;
+ } // release lock
+}
+
+void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
+ InputState::CancelationOptions options, const char* reason) {
+ for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) {
+ synthesizeCancelationEventsForConnectionLocked(
+ mConnectionsByReceiveFd.valueAt(i), options, reason);
+ }
+}
+
+void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
+ const sp<InputChannel>& channel, InputState::CancelationOptions options,
+ const char* reason) {
+ ssize_t index = getConnectionIndexLocked(channel);
+ if (index >= 0) {
+ synthesizeCancelationEventsForConnectionLocked(
+ mConnectionsByReceiveFd.valueAt(index), options, reason);
+ }
+}
+
+void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
+ const sp<Connection>& connection, InputState::CancelationOptions options,
+ const char* reason) {
+ nsecs_t currentTime = now();
+
+ mTempCancelationEvents.clear();
+ connection->inputState.synthesizeCancelationEvents(currentTime, & mAllocator,
+ mTempCancelationEvents, options);
+
+ if (! mTempCancelationEvents.isEmpty()
+ && connection->status != Connection::STATUS_BROKEN) {
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+ LOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync "
+ "with reality: %s, options=%d.",
+ connection->getInputChannelName(), mTempCancelationEvents.size(), reason, options);
+#endif
+ for (size_t i = 0; i < mTempCancelationEvents.size(); i++) {
+ EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i);
+ switch (cancelationEventEntry->type) {
+ case EventEntry::TYPE_KEY:
+ logOutboundKeyDetailsLocked("cancel - ",
+ static_cast<KeyEntry*>(cancelationEventEntry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ logOutboundMotionDetailsLocked("cancel - ",
+ static_cast<MotionEntry*>(cancelationEventEntry));
+ break;
+ }
+
+ int32_t xOffset, yOffset;
+ const InputWindow* window = getWindowLocked(connection->inputChannel);
+ if (window) {
+ xOffset = -window->frameLeft;
+ yOffset = -window->frameTop;
+ } else {
+ xOffset = 0;
+ yOffset = 0;
+ }
+
+ DispatchEntry* cancelationDispatchEntry =
+ mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref
+ 0, xOffset, yOffset);
+ connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+
+ mAllocator.releaseEventEntry(cancelationEventEntry);
+ }
+
+ if (!connection->outboundQueue.headSentinel.next->inProgress) {
+ startDispatchCycleLocked(currentTime, connection);
+ }
+ }
+}
+
+InputDispatcher::MotionEntry*
+InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
+ assert(pointerIds.value != 0);
+
+ uint32_t splitPointerIndexMap[MAX_POINTERS];
+ int32_t splitPointerIds[MAX_POINTERS];
+ PointerCoords splitPointerCoords[MAX_POINTERS];
+
+ uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+ uint32_t splitPointerCount = 0;
+
+ for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
+ originalPointerIndex++) {
+ int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]);
+ if (pointerIds.hasBit(pointerId)) {
+ splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
+ splitPointerIds[splitPointerCount] = pointerId;
+ splitPointerCoords[splitPointerCount] =
+ originalMotionEntry->firstSample.pointerCoords[originalPointerIndex];
+ splitPointerCount += 1;
+ }
+ }
+ assert(splitPointerCount == pointerIds.count());
+
+ int32_t action = originalMotionEntry->action;
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+ || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
+ int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex];
+ if (pointerIds.hasBit(pointerId)) {
+ if (pointerIds.count() == 1) {
+ // The first/last pointer went down/up.
+ action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
+ ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else {
+ // A secondary pointer went down/up.
+ uint32_t splitPointerIndex = 0;
+ while (pointerId != splitPointerIds[splitPointerIndex]) {
+ splitPointerIndex += 1;
+ }
+ action = maskedAction | (splitPointerIndex
+ << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+ } else {
+ // An unrelated pointer changed.
+ action = AMOTION_EVENT_ACTION_MOVE;
+ }
+ }
+
+ MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry(
+ originalMotionEntry->eventTime,
+ originalMotionEntry->deviceId,
+ originalMotionEntry->source,
+ originalMotionEntry->policyFlags,
+ action,
+ originalMotionEntry->flags,
+ originalMotionEntry->metaState,
+ originalMotionEntry->edgeFlags,
+ originalMotionEntry->xPrecision,
+ originalMotionEntry->yPrecision,
+ originalMotionEntry->downTime,
+ splitPointerCount, splitPointerIds, splitPointerCoords);
+
+ for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next;
+ originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) {
+ for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount;
+ splitPointerIndex++) {
+ uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex];
+ splitPointerCoords[splitPointerIndex] =
+ originalMotionSample->pointerCoords[originalPointerIndex];
+ }
+
+ mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime,
+ splitPointerCoords);
+ }
+
+ return splitMotionEntry;
+}
+
+void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime);
+#endif
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
+}
+
+void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
+ "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
+ eventTime, deviceId, source, policyFlags, action, flags,
+ keyCode, scanCode, metaState, downTime);
+#endif
+ if (! validateKeyEvent(action)) {
+ return;
+ }
+
+ policyFlags |= POLICY_FLAG_TRUSTED;
+ mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
+ keyCode, scanCode, /*byref*/ policyFlags);
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ int32_t repeatCount = 0;
+ KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
+ deviceId, source, policyFlags, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime);
+
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
+}
+
+void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags,
+ uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
+ float xPrecision, float yPrecision, nsecs_t downTime) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
+ "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, "
+ "xPrecision=%f, yPrecision=%f, downTime=%lld",
+ eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
+ xPrecision, yPrecision, downTime);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y,
+ pointerCoords[i].pressure, pointerCoords[i].size,
+ pointerCoords[i].touchMajor, pointerCoords[i].touchMinor,
+ pointerCoords[i].toolMajor, pointerCoords[i].toolMinor,
+ pointerCoords[i].orientation);
+ }
+#endif
+ if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+ return;
+ }
+
+ policyFlags |= POLICY_FLAG_TRUSTED;
+ mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
+
+ bool needWake;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Attempt batching and streaming of move events.
+ if (action == AMOTION_EVENT_ACTION_MOVE) {
+ // BATCHING CASE
+ //
+ // Try to append a move sample to the tail of the inbound queue for this device.
+ // Give up if we encounter a non-move motion event for this device since that
+ // means we cannot append any new samples until a new motion event has started.
+ for (EventEntry* entry = mInboundQueue.tailSentinel.prev;
+ entry != & mInboundQueue.headSentinel; entry = entry->prev) {
+ if (entry->type != EventEntry::TYPE_MOTION) {
+ // Keep looking for motion events.
+ continue;
+ }
+
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ if (motionEntry->deviceId != deviceId) {
+ // Keep looking for this device.
+ continue;
+ }
+
+ if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || motionEntry->pointerCount != pointerCount
+ || motionEntry->isInjected()) {
+ // Last motion event in the queue for this device is not compatible for
+ // appending new samples. Stop here.
+ goto NoBatchingOrStreaming;
+ }
+
+ // The last motion event is a move and is compatible for appending.
+ // Do the batching magic.
+ mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recent "
+ "motion event for this device in the inbound queue.");
+#endif
+ return; // done!
+ }
+
+ // STREAMING CASE
+ //
+ // There is no pending motion event (of any kind) for this device in the inbound queue.
+ // Search the outbound queue for the current foreground targets to find a dispatched
+ // motion event that is still in progress. If found, then, appen the new sample to
+ // that event and push it out to all current targets. The logic in
+ // prepareDispatchCycleLocked takes care of the case where some targets may
+ // already have consumed the motion event by starting a new dispatch cycle if needed.
+ if (mCurrentInputTargetsValid) {
+ for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
+ const InputTarget& inputTarget = mCurrentInputTargets[i];
+ if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) {
+ // Skip non-foreground targets. We only want to stream if there is at
+ // least one foreground target whose dispatch is still in progress.
+ continue;
+ }
+
+ ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
+ if (connectionIndex < 0) {
+ // Connection must no longer be valid.
+ continue;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->outboundQueue.isEmpty()) {
+ // This foreground target has an empty outbound queue.
+ continue;
+ }
+
+ DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
+ if (! dispatchEntry->inProgress
+ || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION
+ || dispatchEntry->isSplit()) {
+ // No motion event is being dispatched, or it is being split across
+ // windows in which case we cannot stream.
+ continue;
+ }
+
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(
+ dispatchEntry->eventEntry);
+ if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE
+ || motionEntry->deviceId != deviceId
+ || motionEntry->pointerCount != pointerCount
+ || motionEntry->isInjected()) {
+ // The motion event is not compatible with this move.
+ continue;
+ }
+
+ // Hurray! This foreground target is currently dispatching a move event
+ // that we can stream onto. Append the motion sample and resume dispatch.
+ mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
+#if DEBUG_BATCHING
+ LOGD("Appended motion sample onto batch for most recently dispatched "
+ "motion event for this device in the outbound queues. "
+ "Attempting to stream the motion sample.");
+#endif
+ nsecs_t currentTime = now();
+ dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry,
+ true /*resumeWithAppendedMotionSample*/);
+
+ runCommandsLockedInterruptible();
+ return; // done!
+ }
+ }
+
+NoBatchingOrStreaming:;
+ }
+
+ // Just enqueue a new motion event.
+ MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
+ deviceId, source, policyFlags, action, flags, metaState, edgeFlags,
+ xPrecision, yPrecision, downTime,
+ pointerCount, pointerIds, pointerCoords);
+
+ needWake = enqueueInboundEventLocked(newEntry);
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
+}
+
+void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
+ uint32_t policyFlags) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x",
+ switchCode, switchValue, policyFlags);
+#endif
+
+ policyFlags |= POLICY_FLAG_TRUSTED;
+ mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags);
+}
+
+int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+#if DEBUG_INBOUND_EVENT_DETAILS
+ LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+ "syncMode=%d, timeoutMillis=%d",
+ event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis);
+#endif
+
+ nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+
+ uint32_t policyFlags = POLICY_FLAG_INJECTED;
+ if (hasInjectionPermission(injectorPid, injectorUid)) {
+ policyFlags |= POLICY_FLAG_TRUSTED;
+ }
+
+ EventEntry* injectedEntry;
+ switch (event->getType()) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
+ int32_t action = keyEvent->getAction();
+ if (! validateKeyEvent(action)) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ nsecs_t eventTime = keyEvent->getEventTime();
+ int32_t deviceId = keyEvent->getDeviceId();
+ int32_t flags = keyEvent->getFlags();
+ int32_t keyCode = keyEvent->getKeyCode();
+ int32_t scanCode = keyEvent->getScanCode();
+ mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
+ keyCode, scanCode, /*byref*/ policyFlags);
+
+ mLock.lock();
+ injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(),
+ policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(),
+ keyEvent->getRepeatCount(), keyEvent->getDownTime());
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+ int32_t action = motionEvent->getAction();
+ size_t pointerCount = motionEvent->getPointerCount();
+ const int32_t* pointerIds = motionEvent->getPointerIds();
+ if (! validateMotionEvent(action, pointerCount, pointerIds)) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ nsecs_t eventTime = motionEvent->getEventTime();
+ mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
+
+ mLock.lock();
+ const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
+ const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
+ MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
+ motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags,
+ action, motionEvent->getFlags(),
+ motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+ motionEvent->getDownTime(), uint32_t(pointerCount),
+ pointerIds, samplePointerCoords);
+ for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
+ sampleEventTimes += 1;
+ samplePointerCoords += pointerCount;
+ mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
+ }
+ injectedEntry = motionEntry;
+ break;
+ }
+
+ default:
+ LOGW("Cannot inject event of type %d", event->getType());
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ InjectionState* injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid);
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectionState->injectionIsAsync = true;
+ }
+
+ injectionState->refCount += 1;
+ injectedEntry->injectionState = injectionState;
+
+ bool needWake = enqueueInboundEventLocked(injectedEntry);
+ mLock.unlock();
+
+ if (needWake) {
+ mLooper->wake();
+ }
+
+ int32_t injectionResult;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
+ injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ } else {
+ for (;;) {
+ injectionResult = injectionState->injectionResult;
+ if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
+ break;
+ }
+
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for injection result "
+ "to become available.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ }
+
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
+ && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) {
+ while (injectionState->pendingForegroundDispatches != 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+ injectionState->pendingForegroundDispatches);
+#endif
+ nsecs_t remainingTimeout = endTime - now();
+ if (remainingTimeout <= 0) {
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Timed out waiting for pending foreground "
+ "dispatches to finish.");
+#endif
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+
+ mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ }
+ }
+ }
+
+ mAllocator.releaseInjectionState(injectionState);
+ } // release lock
+
+#if DEBUG_INJECTION
+ LOGD("injectInputEvent - Finished with result %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
+#endif
+
+ return injectionResult;
+}
+
+bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
+ return injectorUid == 0
+ || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
+}
+
+void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
+#if DEBUG_INJECTION
+ LOGD("Setting input event injection result to %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+#endif
+
+ if (injectionState->injectionIsAsync) {
+ // Log the outcome since the injector did not wait for the injection result.
+ switch (injectionResult) {
+ case INPUT_EVENT_INJECTION_SUCCEEDED:
+ LOGV("Asynchronous input event injection succeeded.");
+ break;
+ case INPUT_EVENT_INJECTION_FAILED:
+ LOGW("Asynchronous input event injection failed.");
+ break;
+ case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
+ LOGW("Asynchronous input event injection permission denied.");
+ break;
+ case INPUT_EVENT_INJECTION_TIMED_OUT:
+ LOGW("Asynchronous input event injection timed out.");
+ break;
+ }
+ }
+
+ injectionState->injectionResult = injectionResult;
+ mInjectionResultAvailableCondition.broadcast();
+ }
+}
+
+void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
+ injectionState->pendingForegroundDispatches += 1;
+ }
+}
+
+void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+ InjectionState* injectionState = entry->injectionState;
+ if (injectionState) {
+ injectionState->pendingForegroundDispatches -= 1;
+
+ if (injectionState->pendingForegroundDispatches == 0) {
+ mInjectionSyncFinishedCondition.broadcast();
+ }
+ }
+}
+
+const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) {
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ const InputWindow* window = & mWindows[i];
+ if (window->inputChannel == inputChannel) {
+ return window;
+ }
+ }
+ return NULL;
+}
+
+void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Clear old window pointers.
+ sp<InputChannel> oldFocusedWindowChannel;
+ if (mFocusedWindow) {
+ oldFocusedWindowChannel = mFocusedWindow->inputChannel;
+ mFocusedWindow = NULL;
+ }
+
+ mWindows.clear();
+
+ // Loop over new windows and rebuild the necessary window pointers for
+ // tracking focus and touch.
+ mWindows.appendVector(inputWindows);
+
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ const InputWindow* window = & mWindows.itemAt(i);
+ if (window->hasFocus) {
+ mFocusedWindow = window;
+ break;
+ }
+ }
+
+ if (oldFocusedWindowChannel != NULL) {
+ if (!mFocusedWindow || oldFocusedWindowChannel != mFocusedWindow->inputChannel) {
+#if DEBUG_FOCUS
+ LOGD("Focus left window: %s",
+ oldFocusedWindowChannel->getName().string());
+#endif
+ synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel,
+ InputState::CANCEL_NON_POINTER_EVENTS, "focus left window");
+ oldFocusedWindowChannel.clear();
+ }
+ }
+ if (mFocusedWindow && oldFocusedWindowChannel == NULL) {
+#if DEBUG_FOCUS
+ LOGD("Focus entered window: %s",
+ mFocusedWindow->inputChannel->getName().string());
+#endif
+ }
+
+ for (size_t i = 0; i < mTouchState.windows.size(); ) {
+ TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i);
+ const InputWindow* window = getWindowLocked(touchedWindow.channel);
+ if (window) {
+ touchedWindow.window = window;
+ i += 1;
+ } else {
+#if DEBUG_FOCUS
+ LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string());
+#endif
+ synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel,
+ InputState::CANCEL_POINTER_EVENTS, "touched window was removed");
+ mTouchState.windows.removeAt(i);
+ }
+ }
+
+#if DEBUG_FOCUS
+ //logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
+void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ releaseFocusedApplicationLocked();
+
+ if (inputApplication) {
+ mFocusedApplicationStorage = *inputApplication;
+ mFocusedApplication = & mFocusedApplicationStorage;
+ }
+
+#if DEBUG_FOCUS
+ //logDispatchStateLocked();
+#endif
+ } // release lock
+
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+}
+
+void InputDispatcher::releaseFocusedApplicationLocked() {
+ if (mFocusedApplication) {
+ mFocusedApplication = NULL;
+ mFocusedApplicationStorage.handle.clear();
+ }
+}
+
+void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ bool changed;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ if (mDispatchFrozen && !frozen) {
+ resetANRTimeoutsLocked();
+ }
+
+ if (mDispatchEnabled && !enabled) {
+ resetAndDropEverythingLocked("dispatcher is being disabled");
+ }
+
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+ changed = true;
+ } else {
+ changed = false;
+ }
+
+#if DEBUG_FOCUS
+ //logDispatchStateLocked();
+#endif
+ } // release lock
+
+ if (changed) {
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
+ }
+}
+
+void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
+#if DEBUG_FOCUS
+ LOGD("Resetting and dropping all events (%s).", reason);
+#endif
+
+ synthesizeCancelationEventsForAllConnectionsLocked(InputState::CANCEL_ALL_EVENTS, reason);
+
+ resetKeyRepeatLocked();
+ releasePendingEventLocked();
+ drainInboundQueueLocked();
+ resetTargetsLocked();
+
+ mTouchState.reset();
+}
+
+void InputDispatcher::logDispatchStateLocked() {
+ String8 dump;
+ dumpDispatchStateLocked(dump);
+
+ char* text = dump.lockBuffer(dump.size());
+ char* start = text;
+ while (*start != '\0') {
+ char* end = strchr(start, '\n');
+ if (*end == '\n') {
+ *(end++) = '\0';
+ }
+ LOGD("%s", start);
+ start = end;
+ }
+}
+
+void InputDispatcher::dumpDispatchStateLocked(String8& dump) {
+ dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled);
+ dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen);
+
+ if (mFocusedApplication) {
+ dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ dump.append(INDENT "FocusedApplication: <null>\n");
+ }
+ dump.appendFormat(INDENT "FocusedWindow: name='%s'\n",
+ mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>");
+
+ dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down));
+ dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
+ if (!mTouchState.windows.isEmpty()) {
+ dump.append(INDENT "TouchedWindows:\n");
+ for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTouchState.windows[i];
+ dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+ i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value,
+ touchedWindow.targetFlags);
+ }
+ } else {
+ dump.append(INDENT "TouchedWindows: <none>\n");
+ }
+
+ if (!mWindows.isEmpty()) {
+ dump.append(INDENT "Windows:\n");
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ const InputWindow& window = mWindows[i];
+ dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+ "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
+ "frame=[%d,%d][%d,%d], "
+ "visibleFrame=[%d,%d][%d,%d], "
+ "touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+ i, window.name.string(),
+ toString(window.paused),
+ toString(window.hasFocus),
+ toString(window.hasWallpaper),
+ toString(window.visible),
+ toString(window.canReceiveKeys),
+ window.layoutParamsFlags, window.layoutParamsType,
+ window.layer,
+ window.frameLeft, window.frameTop,
+ window.frameRight, window.frameBottom,
+ window.visibleFrameLeft, window.visibleFrameTop,
+ window.visibleFrameRight, window.visibleFrameBottom,
+ window.touchableAreaLeft, window.touchableAreaTop,
+ window.touchableAreaRight, window.touchableAreaBottom,
+ window.ownerPid, window.ownerUid,
+ window.dispatchingTimeout / 1000000.0);
+ }
+ } else {
+ dump.append(INDENT "Windows: <none>\n");
+ }
+
+ if (!mMonitoringChannels.isEmpty()) {
+ dump.append(INDENT "MonitoringChannels:\n");
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ const sp<InputChannel>& channel = mMonitoringChannels[i];
+ dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string());
+ }
+ } else {
+ dump.append(INDENT "MonitoringChannels: <none>\n");
+ }
+
+ dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
+
+ if (!mActiveConnections.isEmpty()) {
+ dump.append(INDENT "ActiveConnections:\n");
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ const Connection* connection = mActiveConnections[i];
+ dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u"
+ "inputState.isNeutral=%s\n",
+ i, connection->getInputChannelName(), connection->getStatusLabel(),
+ connection->outboundQueue.count(),
+ toString(connection->inputState.isNeutral()));
+ }
+ } else {
+ dump.append(INDENT "ActiveConnections: <none>\n");
+ }
+
+ if (isAppSwitchPendingLocked()) {
+ dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n",
+ (mAppSwitchDueTime - now()) / 1000000.0);
+ } else {
+ dump.append(INDENT "AppSwitch: not pending\n");
+ }
+}
+
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
+ toString(monitor));
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (getConnectionIndexLocked(inputChannel) >= 0) {
+ LOGW("Attempted to register already registered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = new Connection(inputChannel);
+ status_t status = connection->initialize();
+ if (status) {
+ LOGE("Failed to initialize input publisher for input channel '%s', status=%d",
+ inputChannel->getName().string(), status);
+ return status;
+ }
+
+ int32_t receiveFd = inputChannel->getReceivePipeFd();
+ mConnectionsByReceiveFd.add(receiveFd, connection);
+
+ if (monitor) {
+ mMonitoringChannels.push(inputChannel);
+ }
+
+ mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+ return OK;
+}
+
+status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+#if DEBUG_REGISTRATION
+ LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
+ if (connectionIndex < 0) {
+ LOGW("Attempted to unregister already unregistered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+
+ connection->status = Connection::STATUS_ZOMBIE;
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+
+ mLooper->removeFd(inputChannel->getReceivePipeFd());
+
+ nsecs_t currentTime = now();
+ abortBrokenDispatchCycleLocked(currentTime, connection);
+
+ runCommandsLockedInterruptible();
+ } // release lock
+
+ // Wake the poll loop because removing the connection may have changed the current
+ // synchronization state.
+ mLooper->wake();
+ return OK;
+}
+
+ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
+void InputDispatcher::activateConnectionLocked(Connection* connection) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ if (mActiveConnections.itemAt(i) == connection) {
+ return;
+ }
+ }
+ mActiveConnections.add(connection);
+}
+
+void InputDispatcher::deactivateConnectionLocked(Connection* connection) {
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ if (mActiveConnections.itemAt(i) == connection) {
+ mActiveConnections.removeAt(i);
+ return;
+ }
+ }
+}
+
+void InputDispatcher::onDispatchCycleStartedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+}
+
+void InputDispatcher::onDispatchCycleFinishedLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+}
+
+void InputDispatcher::onDispatchCycleBrokenLocked(
+ nsecs_t currentTime, const sp<Connection>& connection) {
+ LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+ connection->getInputChannelName());
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+ commandEntry->connection = connection;
+}
+
+void InputDispatcher::onANRLocked(
+ nsecs_t currentTime, const InputApplication* application, const InputWindow* window,
+ nsecs_t eventTime, nsecs_t waitStartTime) {
+ LOGI("Application is not responding: %s. "
+ "%01.1fms since event, %01.1fms since wait started",
+ getApplicationWindowLabelLocked(application, window).string(),
+ (currentTime - eventTime) / 1000000.0,
+ (currentTime - waitStartTime) / 1000000.0);
+
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyANRLockedInterruptible);
+ if (application) {
+ commandEntry->inputApplicationHandle = application->handle;
+ }
+ if (window) {
+ commandEntry->inputChannel = window->inputChannel;
+ }
+}
+
+void InputDispatcher::doNotifyConfigurationChangedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
+
+ mLock.lock();
+}
+
+void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<Connection> connection = commandEntry->connection;
+
+ if (connection->status != Connection::STATUS_ZOMBIE) {
+ mLock.unlock();
+
+ mPolicy->notifyInputChannelBroken(connection->inputChannel);
+
+ mLock.lock();
+ }
+}
+
+void InputDispatcher::doNotifyANRLockedInterruptible(
+ CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ nsecs_t newTimeout = mPolicy->notifyANR(
+ commandEntry->inputApplicationHandle, commandEntry->inputChannel);
+
+ mLock.lock();
+
+ resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
+}
+
+void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
+ CommandEntry* commandEntry) {
+ KeyEntry* entry = commandEntry->keyEntry;
+ mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags,
+ entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
+ entry->downTime, entry->eventTime);
+
+ mLock.unlock();
+
+ bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel,
+ & mReusableKeyEvent, entry->policyFlags);
+
+ mLock.lock();
+
+ entry->interceptKeyResult = consumed
+ ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
+ : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ mAllocator.releaseKeyEntry(entry);
+}
+
+void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
+ mLock.unlock();
+
+ mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+
+ mLock.lock();
+}
+
+void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry,
+ int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) {
+ // TODO Write some statistics about how long we spend waiting.
+}
+
+void InputDispatcher::dump(String8& dump) {
+ dump.append("Input Dispatcher State:\n");
+ dumpDispatchStateLocked(dump);
+}
+
+
+// --- InputDispatcher::Queue ---
+
+template <typename T>
+uint32_t InputDispatcher::Queue<T>::count() const {
+ uint32_t result = 0;
+ for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) {
+ result += 1;
+ }
+ return result;
+}
+
+
+// --- InputDispatcher::Allocator ---
+
+InputDispatcher::Allocator::Allocator() {
+}
+
+InputDispatcher::InjectionState*
+InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) {
+ InjectionState* injectionState = mInjectionStatePool.alloc();
+ injectionState->refCount = 1;
+ injectionState->injectorPid = injectorPid;
+ injectionState->injectorUid = injectorUid;
+ injectionState->injectionIsAsync = false;
+ injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING;
+ injectionState->pendingForegroundDispatches = 0;
+ return injectionState;
+}
+
+void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
+ nsecs_t eventTime, uint32_t policyFlags) {
+ entry->type = type;
+ entry->refCount = 1;
+ entry->dispatchInProgress = false;
+ entry->eventTime = eventTime;
+ entry->policyFlags = policyFlags;
+ entry->injectionState = NULL;
+}
+
+void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) {
+ if (entry->injectionState) {
+ releaseInjectionState(entry->injectionState);
+ entry->injectionState = NULL;
+ }
+}
+
+InputDispatcher::ConfigurationChangedEntry*
+InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
+ ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime, 0);
+ return entry;
+}
+
+InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action,
+ int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+ int32_t repeatCount, nsecs_t downTime) {
+ KeyEntry* entry = mKeyEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime, policyFlags);
+
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->action = action;
+ entry->flags = flags;
+ entry->keyCode = keyCode;
+ entry->scanCode = scanCode;
+ entry->metaState = metaState;
+ entry->repeatCount = repeatCount;
+ entry->downTime = downTime;
+ entry->syntheticRepeat = false;
+ entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+ return entry;
+}
+
+InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
+ int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
+ nsecs_t downTime, uint32_t pointerCount,
+ const int32_t* pointerIds, const PointerCoords* pointerCoords) {
+ MotionEntry* entry = mMotionEntryPool.alloc();
+ initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime, policyFlags);
+
+ entry->eventTime = eventTime;
+ entry->deviceId = deviceId;
+ entry->source = source;
+ entry->action = action;
+ entry->flags = flags;
+ entry->metaState = metaState;
+ entry->edgeFlags = edgeFlags;
+ entry->xPrecision = xPrecision;
+ entry->yPrecision = yPrecision;
+ entry->downTime = downTime;
+ entry->pointerCount = pointerCount;
+ entry->firstSample.eventTime = eventTime;
+ entry->firstSample.next = NULL;
+ entry->lastSample = & entry->firstSample;
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ entry->pointerIds[i] = pointerIds[i];
+ entry->firstSample.pointerCoords[i] = pointerCoords[i];
+ }
+ return entry;
+}
+
+InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry(
+ EventEntry* eventEntry,
+ int32_t targetFlags, float xOffset, float yOffset) {
+ DispatchEntry* entry = mDispatchEntryPool.alloc();
+ entry->eventEntry = eventEntry;
+ eventEntry->refCount += 1;
+ entry->targetFlags = targetFlags;
+ entry->xOffset = xOffset;
+ entry->yOffset = yOffset;
+ entry->inProgress = false;
+ entry->headMotionSample = NULL;
+ entry->tailMotionSample = NULL;
+ return entry;
+}
+
+InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) {
+ CommandEntry* entry = mCommandEntryPool.alloc();
+ entry->command = command;
+ return entry;
+}
+
+void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) {
+ injectionState->refCount -= 1;
+ if (injectionState->refCount == 0) {
+ mInjectionStatePool.free(injectionState);
+ } else {
+ assert(injectionState->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) {
+ switch (entry->type) {
+ case EventEntry::TYPE_CONFIGURATION_CHANGED:
+ releaseConfigurationChangedEntry(static_cast<ConfigurationChangedEntry*>(entry));
+ break;
+ case EventEntry::TYPE_KEY:
+ releaseKeyEntry(static_cast<KeyEntry*>(entry));
+ break;
+ case EventEntry::TYPE_MOTION:
+ releaseMotionEntry(static_cast<MotionEntry*>(entry));
+ break;
+ default:
+ assert(false);
+ break;
+ }
+}
+
+void InputDispatcher::Allocator::releaseConfigurationChangedEntry(
+ ConfigurationChangedEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
+ mConfigurationChangeEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
+ mKeyEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) {
+ entry->refCount -= 1;
+ if (entry->refCount == 0) {
+ releaseEventEntryInjectionState(entry);
+ for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) {
+ MotionSample* next = sample->next;
+ mMotionSamplePool.free(sample);
+ sample = next;
+ }
+ mMotionEntryPool.free(entry);
+ } else {
+ assert(entry->refCount > 0);
+ }
+}
+
+void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) {
+ releaseEventEntry(entry->eventEntry);
+ mDispatchEntryPool.free(entry);
+}
+
+void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) {
+ mCommandEntryPool.free(entry);
+}
+
+void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
+ nsecs_t eventTime, const PointerCoords* pointerCoords) {
+ MotionSample* sample = mMotionSamplePool.alloc();
+ sample->eventTime = eventTime;
+ uint32_t pointerCount = motionEntry->pointerCount;
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ sample->pointerCoords[i] = pointerCoords[i];
+ }
+
+ sample->next = NULL;
+ motionEntry->lastSample->next = sample;
+ motionEntry->lastSample = sample;
+}
+
+void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) {
+ releaseEventEntryInjectionState(keyEntry);
+
+ keyEntry->dispatchInProgress = false;
+ keyEntry->syntheticRepeat = false;
+ keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
+}
+
+
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+ uint32_t count = 1;
+ for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+ count += 1;
+ }
+ return count;
+}
+
+
+// --- InputDispatcher::InputState ---
+
+InputDispatcher::InputState::InputState() {
+}
+
+InputDispatcher::InputState::~InputState() {
+}
+
+bool InputDispatcher::InputState::isNeutral() const {
+ return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent(
+ const EventEntry* entry) {
+ switch (entry->type) {
+ case EventEntry::TYPE_KEY:
+ return trackKey(static_cast<const KeyEntry*>(entry));
+
+ case EventEntry::TYPE_MOTION:
+ return trackMotion(static_cast<const MotionEntry*>(entry));
+
+ default:
+ return CONSISTENT;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey(
+ const KeyEntry* entry) {
+ int32_t action = entry->action;
+ for (size_t i = 0; i < mKeyMementos.size(); i++) {
+ KeyMemento& memento = mKeyMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source
+ && memento.keyCode == entry->keyCode
+ && memento.scanCode == entry->scanCode) {
+ switch (action) {
+ case AKEY_EVENT_ACTION_UP:
+ mKeyMementos.removeAt(i);
+ return CONSISTENT;
+
+ case AKEY_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AKEY_EVENT_ACTION_DOWN: {
+ mKeyMementos.push();
+ KeyMemento& memento = mKeyMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.keyCode = entry->keyCode;
+ memento.scanCode = entry->scanCode;
+ memento.downTime = entry->downTime;
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion(
+ const MotionEntry* entry) {
+ int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK;
+ for (size_t i = 0; i < mMotionMementos.size(); i++) {
+ MotionMemento& memento = mMotionMementos.editItemAt(i);
+ if (memento.deviceId == entry->deviceId
+ && memento.source == entry->source) {
+ switch (action) {
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ mMotionMementos.removeAt(i);
+ return CONSISTENT;
+
+ case AMOTION_EVENT_ACTION_DOWN:
+ return TOLERABLE;
+
+ case AMOTION_EVENT_ACTION_POINTER_DOWN:
+ if (entry->pointerCount == memento.pointerCount + 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_POINTER_UP:
+ if (entry->pointerCount == memento.pointerCount - 1) {
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ case AMOTION_EVENT_ACTION_MOVE:
+ if (entry->pointerCount == memento.pointerCount) {
+ return CONSISTENT;
+ }
+ return BROKEN;
+
+ default:
+ return BROKEN;
+ }
+ }
+ }
+
+ switch (action) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ mMotionMementos.push();
+ MotionMemento& memento = mMotionMementos.editTop();
+ memento.deviceId = entry->deviceId;
+ memento.source = entry->source;
+ memento.xPrecision = entry->xPrecision;
+ memento.yPrecision = entry->yPrecision;
+ memento.downTime = entry->downTime;
+ memento.setPointers(entry);
+ return CONSISTENT;
+ }
+
+ default:
+ return BROKEN;
+ }
+}
+
+void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
+ pointerCount = entry->pointerCount;
+ for (uint32_t i = 0; i < entry->pointerCount; i++) {
+ pointerIds[i] = entry->pointerIds[i];
+ pointerCoords[i] = entry->lastSample->pointerCoords[i];
+ }
+}
+
+void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
+ Allocator* allocator, Vector<EventEntry*>& outEvents,
+ CancelationOptions options) {
+ for (size_t i = 0; i < mKeyMementos.size(); ) {
+ const KeyMemento& memento = mKeyMementos.itemAt(i);
+ if (shouldCancelEvent(memento.source, options)) {
+ outEvents.push(allocator->obtainKeyEntry(currentTime,
+ memento.deviceId, memento.source, 0,
+ AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
+ mKeyMementos.removeAt(i);
+ } else {
+ i += 1;
+ }
+ }
+
+ for (size_t i = 0; i < mMotionMementos.size(); ) {
+ const MotionMemento& memento = mMotionMementos.itemAt(i);
+ if (shouldCancelEvent(memento.source, options)) {
+ outEvents.push(allocator->obtainMotionEntry(currentTime,
+ memento.deviceId, memento.source, 0,
+ AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0,
+ memento.xPrecision, memento.yPrecision, memento.downTime,
+ memento.pointerCount, memento.pointerIds, memento.pointerCoords));
+ mMotionMementos.removeAt(i);
+ } else {
+ i += 1;
+ }
+ }
+}
+
+void InputDispatcher::InputState::clear() {
+ mKeyMementos.clear();
+ mMotionMementos.clear();
+}
+
+bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource,
+ CancelationOptions options) {
+ switch (options) {
+ case CANCEL_POINTER_EVENTS:
+ return eventSource & AINPUT_SOURCE_CLASS_POINTER;
+ case CANCEL_NON_POINTER_EVENTS:
+ return !(eventSource & AINPUT_SOURCE_CLASS_POINTER);
+ default:
+ return true;
+ }
+}
+
+
+// --- InputDispatcher::Connection ---
+
+InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
+ status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel),
+ lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) {
+}
+
+InputDispatcher::Connection::~Connection() {
+}
+
+status_t InputDispatcher::Connection::initialize() {
+ return inputPublisher.initialize();
+}
+
+const char* InputDispatcher::Connection::getStatusLabel() const {
+ switch (status) {
+ case STATUS_NORMAL:
+ return "NORMAL";
+
+ case STATUS_BROKEN:
+ return "BROKEN";
+
+ case STATUS_ZOMBIE:
+ return "ZOMBIE";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent(
+ const EventEntry* eventEntry) const {
+ for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev;
+ dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) {
+ if (dispatchEntry->eventEntry == eventEntry) {
+ return dispatchEntry;
+ }
+ }
+ return NULL;
+}
+
+
+// --- InputDispatcher::CommandEntry ---
+
+InputDispatcher::CommandEntry::CommandEntry() :
+ keyEntry(NULL) {
+}
+
+InputDispatcher::CommandEntry::~CommandEntry() {
+}
+
+
+// --- InputDispatcher::TouchState ---
+
+InputDispatcher::TouchState::TouchState() :
+ down(false), split(false) {
+}
+
+InputDispatcher::TouchState::~TouchState() {
+}
+
+void InputDispatcher::TouchState::reset() {
+ down = false;
+ split = false;
+ windows.clear();
+}
+
+void InputDispatcher::TouchState::copyFrom(const TouchState& other) {
+ down = other.down;
+ split = other.split;
+ windows.clear();
+ windows.appendVector(other.windows);
+}
+
+void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
+ int32_t targetFlags, BitSet32 pointerIds) {
+ if (targetFlags & InputTarget::FLAG_SPLIT) {
+ split = true;
+ }
+
+ for (size_t i = 0; i < windows.size(); i++) {
+ TouchedWindow& touchedWindow = windows.editItemAt(i);
+ if (touchedWindow.window == window) {
+ touchedWindow.targetFlags |= targetFlags;
+ touchedWindow.pointerIds.value |= pointerIds.value;
+ return;
+ }
+ }
+
+ windows.push();
+
+ TouchedWindow& touchedWindow = windows.editTop();
+ touchedWindow.window = window;
+ touchedWindow.targetFlags = targetFlags;
+ touchedWindow.pointerIds = pointerIds;
+ touchedWindow.channel = window->inputChannel;
+}
+
+void InputDispatcher::TouchState::removeOutsideTouchWindows() {
+ for (size_t i = 0 ; i < windows.size(); ) {
+ if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) {
+ windows.removeAt(i);
+ } else {
+ i += 1;
+ }
+ }
+}
+
+const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() {
+ for (size_t i = 0; i < windows.size(); i++) {
+ if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) {
+ return windows[i].window;
+ }
+ }
+ return NULL;
+}
+
+
+// --- InputDispatcherThread ---
+
+InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
+ Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
+}
+
+InputDispatcherThread::~InputDispatcherThread() {
+}
+
+bool InputDispatcherThread::threadLoop() {
+ mDispatcher->dispatchOnce();
+ return true;
+}
+
+} // namespace android
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
new file mode 100644
index 0000000..09fce38
--- /dev/null
+++ b/libs/ui/InputManager.cpp
@@ -0,0 +1,83 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input manager.
+//
+#define LOG_TAG "InputManager"
+
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <ui/InputManager.h>
+#include <ui/InputReader.h>
+#include <ui/InputDispatcher.h>
+
+namespace android {
+
+InputManager::InputManager(
+ const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& readerPolicy,
+ const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
+ mDispatcher = new InputDispatcher(dispatcherPolicy);
+ mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
+ initialize();
+}
+
+InputManager::InputManager(
+ const sp<InputReaderInterface>& reader,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ mReader(reader),
+ mDispatcher(dispatcher) {
+ initialize();
+}
+
+InputManager::~InputManager() {
+ stop();
+}
+
+void InputManager::initialize() {
+ mReaderThread = new InputReaderThread(mReader);
+ mDispatcherThread = new InputDispatcherThread(mDispatcher);
+}
+
+status_t InputManager::start() {
+ status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ if (result) {
+ LOGE("Could not start InputDispatcher thread due to error %d.", result);
+ return result;
+ }
+
+ result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ if (result) {
+ LOGE("Could not start InputReader thread due to error %d.", result);
+
+ mDispatcherThread->requestExit();
+ return result;
+ }
+
+ return OK;
+}
+
+status_t InputManager::stop() {
+ status_t result = mReaderThread->requestExitAndWait();
+ if (result) {
+ LOGW("Could not stop InputReader thread due to error %d.", result);
+ }
+
+ result = mDispatcherThread->requestExitAndWait();
+ if (result) {
+ LOGW("Could not stop InputDispatcher thread due to error %d.", result);
+ }
+
+ return OK;
+}
+
+sp<InputReaderInterface> InputManager::getReader() {
+ return mReader;
+}
+
+sp<InputDispatcherInterface> InputManager::getDispatcher() {
+ return mDispatcher;
+}
+
+} // namespace android
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
new file mode 100644
index 0000000..3197ab2
--- /dev/null
+++ b/libs/ui/InputReader.cpp
@@ -0,0 +1,3478 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// The input reader.
+//
+#define LOG_TAG "InputReader"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages for each raw event received from the EventHub.
+#define DEBUG_RAW_EVENTS 0
+
+// Log debug messages about touch screen filtering hacks.
+#define DEBUG_HACKS 0
+
+// Log debug messages about virtual key processing.
+#define DEBUG_VIRTUAL_KEYS 0
+
+// Log debug messages about pointers.
+#define DEBUG_POINTERS 0
+
+// Log debug messages about pointer assignment calculations.
+#define DEBUG_POINTER_ASSIGNMENT 0
+
+#include <cutils/log.h>
+#include <ui/InputReader.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+
+#define INDENT " "
+#define INDENT2 " "
+#define INDENT3 " "
+#define INDENT4 " "
+
+namespace android {
+
+// --- Static Functions ---
+
+template<typename T>
+inline static T abs(const T& value) {
+ return value < 0 ? - value : value;
+}
+
+template<typename T>
+inline static T min(const T& a, const T& b) {
+ return a < b ? a : b;
+}
+
+template<typename T>
+inline static void swap(T& a, T& b) {
+ T temp = a;
+ a = b;
+ b = temp;
+}
+
+inline static float avg(float x, float y) {
+ return (x + y) / 2;
+}
+
+inline static float pythag(float x, float y) {
+ return sqrtf(x * x + y * y);
+}
+
+static inline const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+ int32_t mask;
+ switch (keyCode) {
+ case AKEYCODE_ALT_LEFT:
+ mask = AMETA_ALT_LEFT_ON;
+ break;
+ case AKEYCODE_ALT_RIGHT:
+ mask = AMETA_ALT_RIGHT_ON;
+ break;
+ case AKEYCODE_SHIFT_LEFT:
+ mask = AMETA_SHIFT_LEFT_ON;
+ break;
+ case AKEYCODE_SHIFT_RIGHT:
+ mask = AMETA_SHIFT_RIGHT_ON;
+ break;
+ case AKEYCODE_SYM:
+ mask = AMETA_SYM_ON;
+ break;
+ default:
+ return oldMetaState;
+ }
+
+ int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
+ & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
+
+ if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
+ newMetaState |= AMETA_ALT_ON;
+ }
+
+ if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
+ newMetaState |= AMETA_SHIFT_ON;
+ }
+
+ return newMetaState;
+}
+
+static const int32_t keyCodeRotationMap[][4] = {
+ // key codes enumerated counter-clockwise with the original (unrotated) key first
+ // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation
+ { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT },
+ { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
+ { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
+ { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
+};
+static const int keyCodeRotationMapSize =
+ sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
+
+int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
+ if (orientation != InputReaderPolicyInterface::ROTATION_0) {
+ for (int i = 0; i < keyCodeRotationMapSize; i++) {
+ if (keyCode == keyCodeRotationMap[i][0]) {
+ return keyCodeRotationMap[i][orientation];
+ }
+ }
+ }
+ return keyCode;
+}
+
+static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
+ return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0;
+}
+
+
+// --- InputDeviceCalibration ---
+
+InputDeviceCalibration::InputDeviceCalibration() {
+}
+
+void InputDeviceCalibration::clear() {
+ mProperties.clear();
+}
+
+void InputDeviceCalibration::addProperty(const String8& key, const String8& value) {
+ mProperties.add(key, value);
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const {
+ ssize_t index = mProperties.indexOfKey(key);
+ if (index < 0) {
+ return false;
+ }
+
+ outValue = mProperties.valueAt(index);
+ return true;
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ int value = strtol(stringValue.string(), & end, 10);
+ if (*end != '\0') {
+ LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const {
+ String8 stringValue;
+ if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
+ return false;
+ }
+
+ char* end;
+ float value = strtof(stringValue.string(), & end);
+ if (*end != '\0') {
+ LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.",
+ key.string(), stringValue.string());
+ return false;
+ }
+ outValue = value;
+ return true;
+}
+
+
+// --- InputReader ---
+
+InputReader::InputReader(const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+ mGlobalMetaState(0) {
+ configureExcludedDevices();
+ updateGlobalMetaState();
+ updateInputConfiguration();
+}
+
+InputReader::~InputReader() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+}
+
+void InputReader::loopOnce() {
+ RawEvent rawEvent;
+ mEventHub->getEvent(& rawEvent);
+
+#if DEBUG_RAW_EVENTS
+ LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d",
+ rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode,
+ rawEvent.value);
+#endif
+
+ process(& rawEvent);
+}
+
+void InputReader::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EventHubInterface::DEVICE_ADDED:
+ addDevice(rawEvent->deviceId);
+ break;
+
+ case EventHubInterface::DEVICE_REMOVED:
+ removeDevice(rawEvent->deviceId);
+ break;
+
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ handleConfigurationChanged(rawEvent->when);
+ break;
+
+ default:
+ consumeEvent(rawEvent);
+ break;
+ }
+}
+
+void InputReader::addDevice(int32_t deviceId) {
+ String8 name = mEventHub->getDeviceName(deviceId);
+ uint32_t classes = mEventHub->getDeviceClasses(deviceId);
+
+ InputDevice* device = createDevice(deviceId, name, classes);
+ device->configure();
+
+ if (device->isIgnored()) {
+ LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string());
+ } else {
+ LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(),
+ device->getSources());
+ }
+
+ bool added = false;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ mDevices.add(deviceId, device);
+ added = true;
+ }
+ } // release device registry writer lock
+
+ if (! added) {
+ LOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+ delete device;
+ return;
+ }
+}
+
+void InputReader::removeDevice(int32_t deviceId) {
+ bool removed = false;
+ InputDevice* device = NULL;
+ { // acquire device registry writer lock
+ RWLock::AutoWLock _wl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ device = mDevices.valueAt(deviceIndex);
+ mDevices.removeItemsAt(deviceIndex, 1);
+ removed = true;
+ }
+ } // release device registry writer lock
+
+ if (! removed) {
+ LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+ return;
+ }
+
+ if (device->isIgnored()) {
+ LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)",
+ device->getId(), device->getName().string());
+ } else {
+ LOGI("Device removed: id=0x%x, name=%s, sources=%08x",
+ device->getId(), device->getName().string(), device->getSources());
+ }
+
+ device->reset();
+
+ delete device;
+}
+
+InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ InputDevice* device = new InputDevice(this, deviceId, name);
+
+ const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices
+
+ // Switch-like devices.
+ if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+ device->addMapper(new SwitchInputMapper(device));
+ }
+
+ // Keyboard-like devices.
+ uint32_t keyboardSources = 0;
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+ if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+ keyboardSources |= AINPUT_SOURCE_KEYBOARD;
+ }
+ if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+ keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+ }
+ if (classes & INPUT_DEVICE_CLASS_DPAD) {
+ keyboardSources |= AINPUT_SOURCE_DPAD;
+ }
+
+ if (keyboardSources != 0) {
+ device->addMapper(new KeyboardInputMapper(device,
+ associatedDisplayId, keyboardSources, keyboardType));
+ }
+
+ // Trackball-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
+ device->addMapper(new TrackballInputMapper(device, associatedDisplayId));
+ }
+
+ // Touchscreen-like devices.
+ if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) {
+ device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId));
+ } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) {
+ device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId));
+ }
+
+ return device;
+}
+
+void InputReader::consumeEvent(const RawEvent* rawEvent) {
+ int32_t deviceId = rawEvent->deviceId;
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ LOGW("Discarding event for unknown deviceId %d.", deviceId);
+ return;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ //LOGD("Discarding event for ignored deviceId %d.", deviceId);
+ return;
+ }
+
+ device->process(rawEvent);
+ } // release device registry reader lock
+}
+
+void InputReader::handleConfigurationChanged(nsecs_t when) {
+ // Reset global meta state because it depends on the list of all configured devices.
+ updateGlobalMetaState();
+
+ // Update input configuration.
+ updateInputConfiguration();
+
+ // Enqueue configuration changed.
+ mDispatcher->notifyConfigurationChanged(when);
+}
+
+void InputReader::configureExcludedDevices() {
+ Vector<String8> excludedDeviceNames;
+ mPolicy->getExcludedDeviceNames(excludedDeviceNames);
+
+ for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
+ mEventHub->addExcludedDevice(excludedDeviceNames[i]);
+ }
+}
+
+void InputReader::updateGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ mGlobalMetaState = 0;
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ mGlobalMetaState |= device->getMetaState();
+ }
+ } // release device registry reader lock
+ } // release state lock
+}
+
+int32_t InputReader::getGlobalMetaState() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ return mGlobalMetaState;
+ } // release state lock
+}
+
+void InputReader::updateInputConfiguration() {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH;
+ int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS;
+ int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV;
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ InputDeviceInfo deviceInfo;
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ device->getDeviceInfo(& deviceInfo);
+ uint32_t sources = deviceInfo.getSources();
+
+ if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) {
+ touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER;
+ }
+ if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) {
+ navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL;
+ } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) {
+ navigationConfig = InputConfiguration::NAVIGATION_DPAD;
+ }
+ if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ keyboardConfig = InputConfiguration::KEYBOARD_QWERTY;
+ }
+ }
+ } // release device registry reader lock
+
+ mInputConfiguration.touchScreen = touchScreenConfig;
+ mInputConfiguration.keyboard = keyboardConfig;
+ mInputConfiguration.navigation = navigationConfig;
+ } // release state lock
+}
+
+void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
+ { // acquire state lock
+ AutoMutex _l(mStateLock);
+
+ *outConfiguration = mInputConfiguration;
+ } // release state lock
+}
+
+status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex < 0) {
+ return NAME_NOT_FOUND;
+ }
+
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (device->isIgnored()) {
+ return NAME_NOT_FOUND;
+ }
+
+ device->getDeviceInfo(outDeviceInfo);
+ return OK;
+ } // release device registy reader lock
+}
+
+void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) {
+ outDeviceIds.clear();
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored()) {
+ outDeviceIds.add(device->getId());
+ }
+ }
+ } // release device registy reader lock
+}
+
+int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t keyCode) {
+ return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState);
+}
+
+int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+ int32_t scanCode) {
+ return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState);
+}
+
+int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) {
+ return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState);
+}
+
+int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code,
+ GetStateFunc getStateFunc) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ int32_t result = AKEY_STATE_UNKNOWN;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = (device->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
+ }
+ }
+ }
+ return result;
+ } // release device registy reader lock
+}
+
+bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
+ size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) {
+ memset(outFlags, 0, numCodes);
+ return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags);
+}
+
+bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+ bool result = false;
+ if (deviceId >= 0) {
+ ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
+ if (deviceIndex >= 0) {
+ InputDevice* device = mDevices.valueAt(deviceIndex);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result = device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
+ } else {
+ size_t numDevices = mDevices.size();
+ for (size_t i = 0; i < numDevices; i++) {
+ InputDevice* device = mDevices.valueAt(i);
+ if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+ result |= device->markSupportedKeyCodes(sourceMask,
+ numCodes, keyCodes, outFlags);
+ }
+ }
+ }
+ return result;
+ } // release device registy reader lock
+}
+
+void InputReader::dump(String8& dump) {
+ mEventHub->dump(dump);
+ dump.append("\n");
+
+ dump.append("Input Reader State:\n");
+
+ { // acquire device registry reader lock
+ RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ mDevices.valueAt(i)->dump(dump);
+ }
+ } // release device registy reader lock
+}
+
+
+// --- InputReaderThread ---
+
+InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
+ Thread(/*canCallJava*/ true), mReader(reader) {
+}
+
+InputReaderThread::~InputReaderThread() {
+}
+
+bool InputReaderThread::threadLoop() {
+ mReader->loopOnce();
+ return true;
+}
+
+
+// --- InputDevice ---
+
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
+ mContext(context), mId(id), mName(name), mSources(0) {
+}
+
+InputDevice::~InputDevice() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ delete mMappers[i];
+ }
+ mMappers.clear();
+}
+
+static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo,
+ int32_t rangeType, const char* name) {
+ const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType);
+ if (range) {
+ dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
+ name, range->min, range->max, range->flat, range->fuzz);
+ }
+}
+
+void InputDevice::dump(String8& dump) {
+ InputDeviceInfo deviceInfo;
+ getDeviceInfo(& deviceInfo);
+
+ dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(),
+ deviceInfo.getName().string());
+ dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
+ dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
+ if (!deviceInfo.getMotionRanges().isEmpty()) {
+ dump.append(INDENT2 "Motion Ranges:\n");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor");
+ dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation");
+ }
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->dump(dump);
+ }
+}
+
+void InputDevice::addMapper(InputMapper* mapper) {
+ mMappers.add(mapper);
+}
+
+void InputDevice::configure() {
+ if (! isIgnored()) {
+ mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration);
+ }
+
+ mSources = 0;
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->configure();
+ mSources |= mapper->getSources();
+ }
+}
+
+void InputDevice::reset() {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->reset();
+ }
+}
+
+void InputDevice::process(const RawEvent* rawEvent) {
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->process(rawEvent);
+ }
+}
+
+void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
+ outDeviceInfo->initialize(mId, mName);
+
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ mapper->populateDeviceInfo(outDeviceInfo);
+ }
+}
+
+int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState);
+}
+
+int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getState(sourceMask, scanCode, & InputMapper::getScanCodeState);
+}
+
+int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getState(sourceMask, switchCode, & InputMapper::getSwitchState);
+}
+
+int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
+ int32_t result = AKEY_STATE_UNKNOWN;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result = (mapper->*getStateFunc)(sourceMask, code);
+ if (result >= AKEY_STATE_DOWN) {
+ return result;
+ }
+ }
+ }
+ return result;
+}
+
+bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ bool result = false;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+ result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+ }
+ }
+ return result;
+}
+
+int32_t InputDevice::getMetaState() {
+ int32_t result = 0;
+ size_t numMappers = mMappers.size();
+ for (size_t i = 0; i < numMappers; i++) {
+ InputMapper* mapper = mMappers[i];
+ result |= mapper->getMetaState();
+ }
+ return result;
+}
+
+
+// --- InputMapper ---
+
+InputMapper::InputMapper(InputDevice* device) :
+ mDevice(device), mContext(device->getContext()) {
+}
+
+InputMapper::~InputMapper() {
+}
+
+void InputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ info->addSource(getSources());
+}
+
+void InputMapper::dump(String8& dump) {
+}
+
+void InputMapper::configure() {
+}
+
+void InputMapper::reset() {
+}
+
+int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return false;
+}
+
+int32_t InputMapper::getMetaState() {
+ return 0;
+}
+
+
+// --- SwitchInputMapper ---
+
+SwitchInputMapper::SwitchInputMapper(InputDevice* device) :
+ InputMapper(device) {
+}
+
+SwitchInputMapper::~SwitchInputMapper() {
+}
+
+uint32_t SwitchInputMapper::getSources() {
+ return 0;
+}
+
+void SwitchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_SW:
+ processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value);
+ break;
+ }
+}
+
+void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) {
+ getDispatcher()->notifySwitch(when, switchCode, switchValue, 0);
+}
+
+int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+}
+
+
+// --- KeyboardInputMapper ---
+
+KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId,
+ uint32_t sources, int32_t keyboardType) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources),
+ mKeyboardType(keyboardType) {
+ initializeLocked();
+}
+
+KeyboardInputMapper::~KeyboardInputMapper() {
+}
+
+void KeyboardInputMapper::initializeLocked() {
+ mLocked.metaState = AMETA_NONE;
+ mLocked.downTime = 0;
+}
+
+uint32_t KeyboardInputMapper::getSources() {
+ return mSources;
+}
+
+void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setKeyboardType(mKeyboardType);
+}
+
+void KeyboardInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Keyboard Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
+ dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size());
+ dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState);
+ dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+ } // release lock
+}
+
+void KeyboardInputMapper::reset() {
+ for (;;) {
+ int32_t keyCode, scanCode;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Synthesize key up event on reset if keys are currently down.
+ if (mLocked.keyDowns.isEmpty()) {
+ initializeLocked();
+ break; // done
+ }
+
+ const KeyDown& keyDown = mLocked.keyDowns.top();
+ keyCode = keyDown.keyCode;
+ scanCode = keyDown.scanCode;
+ } // release lock
+
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ processKey(when, false, keyCode, scanCode, 0);
+ }
+
+ InputMapper::reset();
+ getContext()->updateGlobalMetaState();
+}
+
+void KeyboardInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY: {
+ int32_t scanCode = rawEvent->scanCode;
+ if (isKeyboardOrGamepadKey(scanCode)) {
+ processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode,
+ rawEvent->flags);
+ }
+ break;
+ }
+ }
+}
+
+bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
+ return scanCode < BTN_MOUSE
+ || scanCode >= KEY_OK
+ || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI);
+}
+
+void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
+ int32_t scanCode, uint32_t policyFlags) {
+ int32_t newMetaState;
+ nsecs_t downTime;
+ bool metaStateChanged = false;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (down) {
+ // Rotate key codes according to orientation if needed.
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ if (mAssociatedDisplayId >= 0) {
+ int32_t orientation;
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
+ }
+
+ keyCode = rotateKeyCode(keyCode, orientation);
+ }
+
+ // Add key down.
+ ssize_t keyDownIndex = findKeyDownLocked(scanCode);
+ if (keyDownIndex >= 0) {
+ // key repeat, be sure to use same keycode as before in case of rotation
+ keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
+ } else {
+ // key down
+ mLocked.keyDowns.push();
+ KeyDown& keyDown = mLocked.keyDowns.editTop();
+ keyDown.keyCode = keyCode;
+ keyDown.scanCode = scanCode;
+ }
+
+ mLocked.downTime = when;
+ } else {
+ // Remove key down.
+ ssize_t keyDownIndex = findKeyDownLocked(scanCode);
+ if (keyDownIndex >= 0) {
+ // key up, be sure to use same keycode as before in case of rotation
+ keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode;
+ mLocked.keyDowns.removeAt(size_t(keyDownIndex));
+ } else {
+ // key was not actually down
+ LOGI("Dropping key up from device %s because the key was not down. "
+ "keyCode=%d, scanCode=%d",
+ getDeviceName().string(), keyCode, scanCode);
+ return;
+ }
+ }
+
+ int32_t oldMetaState = mLocked.metaState;
+ newMetaState = updateMetaState(keyCode, down, oldMetaState);
+ if (oldMetaState != newMetaState) {
+ mLocked.metaState = newMetaState;
+ metaStateChanged = true;
+ }
+
+ downTime = mLocked.downTime;
+ } // release lock
+
+ if (metaStateChanged) {
+ getContext()->updateGlobalMetaState();
+ }
+
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+ AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
+}
+
+ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) {
+ size_t n = mLocked.keyDowns.size();
+ for (size_t i = 0; i < n; i++) {
+ if (mLocked.keyDowns[i].scanCode == scanCode) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+}
+
+int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+}
+
+bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+}
+
+int32_t KeyboardInputMapper::getMetaState() {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ return mLocked.metaState;
+ } // release lock
+}
+
+
+// --- TrackballInputMapper ---
+
+TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+ mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+ mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+ mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+
+ initializeLocked();
+}
+
+TrackballInputMapper::~TrackballInputMapper() {
+}
+
+uint32_t TrackballInputMapper::getSources() {
+ return AINPUT_SOURCE_TRACKBALL;
+}
+
+void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+}
+
+void TrackballInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Trackball Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
+ dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
+ dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down));
+ dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime);
+ } // release lock
+}
+
+void TrackballInputMapper::initializeLocked() {
+ mAccumulator.clear();
+
+ mLocked.down = false;
+ mLocked.downTime = 0;
+}
+
+void TrackballInputMapper::reset() {
+ for (;;) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (! mLocked.down) {
+ initializeLocked();
+ break; // done
+ }
+ } // release lock
+
+ // Synthesize trackball button up event on reset.
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = false;
+ sync(when);
+ }
+
+ InputMapper::reset();
+}
+
+void TrackballInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_MOUSE:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
+ mAccumulator.btnMouse = rawEvent->value != 0;
+ // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+ // we need to ensure that we report the up/down promptly.
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+
+ case EV_REL:
+ switch (rawEvent->scanCode) {
+ case REL_X:
+ mAccumulator.fields |= Accumulator::FIELD_REL_X;
+ mAccumulator.relX = rawEvent->value;
+ break;
+ case REL_Y:
+ mAccumulator.fields |= Accumulator::FIELD_REL_Y;
+ mAccumulator.relY = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void TrackballInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
+ int motionEventAction;
+ PointerCoords pointerCoords;
+ nsecs_t downTime;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
+
+ if (downChanged) {
+ if (mAccumulator.btnMouse) {
+ mLocked.down = true;
+ mLocked.downTime = when;
+ } else {
+ mLocked.down = false;
+ }
+ }
+
+ downTime = mLocked.downTime;
+ float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
+ float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
+
+ if (downChanged) {
+ motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ pointerCoords.x = x;
+ pointerCoords.y = y;
+ pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
+ pointerCoords.size = 0;
+ pointerCoords.touchMajor = 0;
+ pointerCoords.touchMinor = 0;
+ pointerCoords.toolMajor = 0;
+ pointerCoords.toolMinor = 0;
+ pointerCoords.orientation = 0;
+
+ if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) {
+ // Rotate motion based on display orientation if needed.
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ int32_t orientation;
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+ return;
+ }
+
+ float temp;
+ switch (orientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ temp = pointerCoords.x;
+ pointerCoords.x = pointerCoords.y;
+ pointerCoords.y = - temp;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_180:
+ pointerCoords.x = - pointerCoords.x;
+ pointerCoords.y = - pointerCoords.y;
+ break;
+
+ case InputReaderPolicyInterface::ROTATION_270:
+ temp = pointerCoords.x;
+ pointerCoords.x = - pointerCoords.y;
+ pointerCoords.y = temp;
+ break;
+ }
+ }
+ } // release lock
+
+ int32_t metaState = mContext->getGlobalMetaState();
+ int32_t pointerId = 0;
+ getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, 0,
+ motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+ 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
+
+ mAccumulator.clear();
+}
+
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
+
+// --- TouchInputMapper ---
+
+TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ InputMapper(device), mAssociatedDisplayId(associatedDisplayId) {
+ mLocked.surfaceOrientation = -1;
+ mLocked.surfaceWidth = -1;
+ mLocked.surfaceHeight = -1;
+
+ initializeLocked();
+}
+
+TouchInputMapper::~TouchInputMapper() {
+}
+
+uint32_t TouchInputMapper::getSources() {
+ return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD;
+}
+
+void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Ensure surface information is up to date so that orientation changes are
+ // noticed immediately.
+ configureSurfaceLocked();
+
+ info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x);
+ info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y);
+
+ if (mLocked.orientedRanges.havePressure) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE,
+ mLocked.orientedRanges.pressure);
+ }
+
+ if (mLocked.orientedRanges.haveSize) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_SIZE,
+ mLocked.orientedRanges.size);
+ }
+
+ if (mLocked.orientedRanges.haveTouchSize) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR,
+ mLocked.orientedRanges.touchMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR,
+ mLocked.orientedRanges.touchMinor);
+ }
+
+ if (mLocked.orientedRanges.haveToolSize) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR,
+ mLocked.orientedRanges.toolMajor);
+ info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR,
+ mLocked.orientedRanges.toolMinor);
+ }
+
+ if (mLocked.orientedRanges.haveOrientation) {
+ info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION,
+ mLocked.orientedRanges.orientation);
+ }
+ } // release lock
+}
+
+void TouchInputMapper::dump(String8& dump) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+ dump.append(INDENT2 "Touch Input Mapper:\n");
+ dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId);
+ dumpParameters(dump);
+ dumpVirtualKeysLocked(dump);
+ dumpRawAxes(dump);
+ dumpCalibration(dump);
+ dumpSurfaceLocked(dump);
+ dump.appendFormat(INDENT3 "Translation and Scaling Factors:");
+ dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin);
+ dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin);
+ dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
+ dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
+ dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision);
+ dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision);
+ dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale);
+ dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale);
+ dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias);
+ dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale);
+ dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias);
+ dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
+ dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
+ dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale);
+ } // release lock
+}
+
+void TouchInputMapper::initializeLocked() {
+ mCurrentTouch.clear();
+ mLastTouch.clear();
+ mDownTime = 0;
+
+ for (uint32_t i = 0; i < MAX_POINTERS; i++) {
+ mAveragingTouchFilter.historyStart[i] = 0;
+ mAveragingTouchFilter.historyEnd[i] = 0;
+ }
+
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+
+ mLocked.currentVirtualKey.down = false;
+
+ mLocked.orientedRanges.havePressure = false;
+ mLocked.orientedRanges.haveSize = false;
+ mLocked.orientedRanges.haveTouchSize = false;
+ mLocked.orientedRanges.haveToolSize = false;
+ mLocked.orientedRanges.haveOrientation = false;
+}
+
+void TouchInputMapper::configure() {
+ InputMapper::configure();
+
+ // Configure basic parameters.
+ configureParameters();
+
+ // Configure absolute axis information.
+ configureRawAxes();
+
+ // Prepare input device calibration.
+ parseCalibration();
+ resolveCalibration();
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Configure surface dimensions and orientation.
+ configureSurfaceLocked();
+ } // release lock
+}
+
+void TouchInputMapper::configureParameters() {
+ mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents();
+ mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents();
+}
+
+void TouchInputMapper::dumpParameters(String8& dump) {
+ dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n",
+ toString(mParameters.useBadTouchFilter));
+ dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n",
+ toString(mParameters.useAveragingTouchFilter));
+ dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n",
+ toString(mParameters.useJumpyTouchFilter));
+}
+
+void TouchInputMapper::configureRawAxes() {
+ mRawAxes.x.clear();
+ mRawAxes.y.clear();
+ mRawAxes.pressure.clear();
+ mRawAxes.touchMajor.clear();
+ mRawAxes.touchMinor.clear();
+ mRawAxes.toolMajor.clear();
+ mRawAxes.toolMinor.clear();
+ mRawAxes.orientation.clear();
+}
+
+static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) {
+ if (axis.valid) {
+ dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n",
+ name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz);
+ } else {
+ dump.appendFormat(INDENT4 "%s: unknown range\n", name);
+ }
+}
+
+void TouchInputMapper::dumpRawAxes(String8& dump) {
+ dump.append(INDENT3 "Raw Axes:\n");
+ dumpAxisInfo(dump, mRawAxes.x, "X");
+ dumpAxisInfo(dump, mRawAxes.y, "Y");
+ dumpAxisInfo(dump, mRawAxes.pressure, "Pressure");
+ dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor");
+ dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor");
+ dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
+ dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
+ dumpAxisInfo(dump, mRawAxes.orientation, "Orientation");
+}
+
+bool TouchInputMapper::configureSurfaceLocked() {
+ // Update orientation and dimensions if needed.
+ int32_t orientation;
+ int32_t width, height;
+ if (mAssociatedDisplayId >= 0) {
+ // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
+ if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) {
+ return false;
+ }
+ } else {
+ orientation = InputReaderPolicyInterface::ROTATION_0;
+ width = mRawAxes.x.getRange();
+ height = mRawAxes.y.getRange();
+ }
+
+ bool orientationChanged = mLocked.surfaceOrientation != orientation;
+ if (orientationChanged) {
+ mLocked.surfaceOrientation = orientation;
+ }
+
+ bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height;
+ if (sizeChanged) {
+ LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d",
+ getDeviceId(), getDeviceName().string(), width, height);
+
+ mLocked.surfaceWidth = width;
+ mLocked.surfaceHeight = height;
+
+ // Configure X and Y factors.
+ if (mRawAxes.x.valid && mRawAxes.y.valid) {
+ mLocked.xOrigin = mRawAxes.x.minValue;
+ mLocked.yOrigin = mRawAxes.y.minValue;
+ mLocked.xScale = float(width) / mRawAxes.x.getRange();
+ mLocked.yScale = float(height) / mRawAxes.y.getRange();
+ mLocked.xPrecision = 1.0f / mLocked.xScale;
+ mLocked.yPrecision = 1.0f / mLocked.yScale;
+
+ configureVirtualKeysLocked();
+ } else {
+ LOGW(INDENT "Touch device did not report support for X or Y axis!");
+ mLocked.xOrigin = 0;
+ mLocked.yOrigin = 0;
+ mLocked.xScale = 1.0f;
+ mLocked.yScale = 1.0f;
+ mLocked.xPrecision = 1.0f;
+ mLocked.yPrecision = 1.0f;
+ }
+
+ // Scale factor for terms that are not oriented in a particular axis.
+ // If the pixels are square then xScale == yScale otherwise we fake it
+ // by choosing an average.
+ mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale);
+
+ // Size of diagonal axis.
+ float diagonalSize = pythag(width, height);
+
+ // TouchMajor and TouchMinor factors.
+ if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) {
+ mLocked.orientedRanges.haveTouchSize = true;
+ mLocked.orientedRanges.touchMajor.min = 0;
+ mLocked.orientedRanges.touchMajor.max = diagonalSize;
+ mLocked.orientedRanges.touchMajor.flat = 0;
+ mLocked.orientedRanges.touchMajor.fuzz = 0;
+ mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor;
+ }
+
+ // ToolMajor and ToolMinor factors.
+ mLocked.toolSizeLinearScale = 0;
+ mLocked.toolSizeLinearBias = 0;
+ mLocked.toolSizeAreaScale = 0;
+ mLocked.toolSizeAreaBias = 0;
+ if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
+ if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) {
+ if (mCalibration.haveToolSizeLinearScale) {
+ mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
+ } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+ mLocked.toolSizeLinearScale = float(min(width, height))
+ / mRawAxes.toolMajor.maxValue;
+ }
+
+ if (mCalibration.haveToolSizeLinearBias) {
+ mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+ }
+ } else if (mCalibration.toolSizeCalibration ==
+ Calibration::TOOL_SIZE_CALIBRATION_AREA) {
+ if (mCalibration.haveToolSizeLinearScale) {
+ mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale;
+ } else {
+ mLocked.toolSizeLinearScale = min(width, height);
+ }
+
+ if (mCalibration.haveToolSizeLinearBias) {
+ mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias;
+ }
+
+ if (mCalibration.haveToolSizeAreaScale) {
+ mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale;
+ } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+ mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue;
+ }
+
+ if (mCalibration.haveToolSizeAreaBias) {
+ mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias;
+ }
+ }
+
+ mLocked.orientedRanges.haveToolSize = true;
+ mLocked.orientedRanges.toolMajor.min = 0;
+ mLocked.orientedRanges.toolMajor.max = diagonalSize;
+ mLocked.orientedRanges.toolMajor.flat = 0;
+ mLocked.orientedRanges.toolMajor.fuzz = 0;
+ mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor;
+ }
+
+ // Pressure factors.
+ mLocked.pressureScale = 0;
+ if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) {
+ RawAbsoluteAxisInfo rawPressureAxis;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ rawPressureAxis = mRawAxes.pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ rawPressureAxis = mRawAxes.touchMajor;
+ break;
+ default:
+ rawPressureAxis.clear();
+ }
+
+ if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL
+ || mCalibration.pressureCalibration
+ == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) {
+ if (mCalibration.havePressureScale) {
+ mLocked.pressureScale = mCalibration.pressureScale;
+ } else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) {
+ mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.havePressure = true;
+ mLocked.orientedRanges.pressure.min = 0;
+ mLocked.orientedRanges.pressure.max = 1.0;
+ mLocked.orientedRanges.pressure.flat = 0;
+ mLocked.orientedRanges.pressure.fuzz = 0;
+ }
+
+ // Size factors.
+ mLocked.sizeScale = 0;
+ if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
+ if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) {
+ if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) {
+ mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.haveSize = true;
+ mLocked.orientedRanges.size.min = 0;
+ mLocked.orientedRanges.size.max = 1.0;
+ mLocked.orientedRanges.size.flat = 0;
+ mLocked.orientedRanges.size.fuzz = 0;
+ }
+
+ // Orientation
+ mLocked.orientationScale = 0;
+ if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) {
+ if (mCalibration.orientationCalibration
+ == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) {
+ if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) {
+ mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue;
+ }
+ }
+
+ mLocked.orientedRanges.orientation.min = - M_PI_2;
+ mLocked.orientedRanges.orientation.max = M_PI_2;
+ mLocked.orientedRanges.orientation.flat = 0;
+ mLocked.orientedRanges.orientation.fuzz = 0;
+ }
+ }
+
+ if (orientationChanged || sizeChanged) {
+ // Compute oriented surface dimensions, precision, and scales.
+ float orientedXScale, orientedYScale;
+ switch (mLocked.surfaceOrientation) {
+ case InputReaderPolicyInterface::ROTATION_90:
+ case InputReaderPolicyInterface::ROTATION_270:
+ mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
+ mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
+ mLocked.orientedXPrecision = mLocked.yPrecision;
+ mLocked.orientedYPrecision = mLocked.xPrecision;
+ orientedXScale = mLocked.yScale;
+ orientedYScale = mLocked.xScale;
+ break;
+ default:
+ mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
+ mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
+ mLocked.orientedXPrecision = mLocked.xPrecision;
+ mLocked.orientedYPrecision = mLocked.yPrecision;
+ orientedXScale = mLocked.xScale;
+ orientedYScale = mLocked.yScale;
+ break;
+ }
+
+ // Configure position ranges.
+ mLocked.orientedRanges.x.min = 0;
+ mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth;
+ mLocked.orientedRanges.x.flat = 0;
+ mLocked.orientedRanges.x.fuzz = orientedXScale;
+
+ mLocked.orientedRanges.y.min = 0;
+ mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight;
+ mLocked.orientedRanges.y.flat = 0;
+ mLocked.orientedRanges.y.fuzz = orientedYScale;
+ }
+
+ return true;
+}
+
+void TouchInputMapper::dumpSurfaceLocked(String8& dump) {
+ dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth);
+ dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight);
+ dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation);
+}
+
+void TouchInputMapper::configureVirtualKeysLocked() {
+ assert(mRawAxes.x.valid && mRawAxes.y.valid);
+
+ // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
+ Vector<VirtualKeyDefinition> virtualKeyDefinitions;
+ getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
+
+ mLocked.virtualKeys.clear();
+
+ if (virtualKeyDefinitions.size() == 0) {
+ return;
+ }
+
+ mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
+
+ int32_t touchScreenLeft = mRawAxes.x.minValue;
+ int32_t touchScreenTop = mRawAxes.y.minValue;
+ int32_t touchScreenWidth = mRawAxes.x.getRange();
+ int32_t touchScreenHeight = mRawAxes.y.getRange();
+
+ for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
+ const VirtualKeyDefinition& virtualKeyDefinition =
+ virtualKeyDefinitions[i];
+
+ mLocked.virtualKeys.add();
+ VirtualKey& virtualKey = mLocked.virtualKeys.editTop();
+
+ virtualKey.scanCode = virtualKeyDefinition.scanCode;
+ int32_t keyCode;
+ uint32_t flags;
+ if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
+ & keyCode, & flags)) {
+ LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
+ virtualKey.scanCode);
+ mLocked.virtualKeys.pop(); // drop the key
+ continue;
+ }
+
+ virtualKey.keyCode = keyCode;
+ virtualKey.flags = flags;
+
+ // convert the key definition's display coordinates into touch coordinates for a hit box
+ int32_t halfWidth = virtualKeyDefinition.width / 2;
+ int32_t halfHeight = virtualKeyDefinition.height / 2;
+
+ virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
+ * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+ virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
+ * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
+ virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
+ * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+ virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
+ * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
+
+ }
+}
+
+void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) {
+ if (!mLocked.virtualKeys.isEmpty()) {
+ dump.append(INDENT3 "Virtual Keys:\n");
+
+ for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i);
+ dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
+ "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
+ i, virtualKey.scanCode, virtualKey.keyCode,
+ virtualKey.hitLeft, virtualKey.hitRight,
+ virtualKey.hitTop, virtualKey.hitBottom);
+ }
+ }
+}
+
+void TouchInputMapper::parseCalibration() {
+ const InputDeviceCalibration& in = getDevice()->getCalibration();
+ Calibration& out = mCalibration;
+
+ // Touch Size
+ out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT;
+ String8 touchSizeCalibrationString;
+ if (in.tryGetProperty(String8("touch.touchSize.calibration"), touchSizeCalibrationString)) {
+ if (touchSizeCalibrationString == "none") {
+ out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE;
+ } else if (touchSizeCalibrationString == "geometric") {
+ out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC;
+ } else if (touchSizeCalibrationString == "pressure") {
+ out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
+ } else if (touchSizeCalibrationString != "default") {
+ LOGW("Invalid value for touch.touchSize.calibration: '%s'",
+ touchSizeCalibrationString.string());
+ }
+ }
+
+ // Tool Size
+ out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_DEFAULT;
+ String8 toolSizeCalibrationString;
+ if (in.tryGetProperty(String8("touch.toolSize.calibration"), toolSizeCalibrationString)) {
+ if (toolSizeCalibrationString == "none") {
+ out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
+ } else if (toolSizeCalibrationString == "geometric") {
+ out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC;
+ } else if (toolSizeCalibrationString == "linear") {
+ out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
+ } else if (toolSizeCalibrationString == "area") {
+ out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_AREA;
+ } else if (toolSizeCalibrationString != "default") {
+ LOGW("Invalid value for touch.toolSize.calibration: '%s'",
+ toolSizeCalibrationString.string());
+ }
+ }
+
+ out.haveToolSizeLinearScale = in.tryGetProperty(String8("touch.toolSize.linearScale"),
+ out.toolSizeLinearScale);
+ out.haveToolSizeLinearBias = in.tryGetProperty(String8("touch.toolSize.linearBias"),
+ out.toolSizeLinearBias);
+ out.haveToolSizeAreaScale = in.tryGetProperty(String8("touch.toolSize.areaScale"),
+ out.toolSizeAreaScale);
+ out.haveToolSizeAreaBias = in.tryGetProperty(String8("touch.toolSize.areaBias"),
+ out.toolSizeAreaBias);
+ out.haveToolSizeIsSummed = in.tryGetProperty(String8("touch.toolSize.isSummed"),
+ out.toolSizeIsSummed);
+
+ // Pressure
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT;
+ String8 pressureCalibrationString;
+ if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) {
+ if (pressureCalibrationString == "none") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ } else if (pressureCalibrationString == "physical") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL;
+ } else if (pressureCalibrationString == "amplitude") {
+ out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ } else if (pressureCalibrationString != "default") {
+ LOGW("Invalid value for touch.pressure.calibration: '%s'",
+ pressureCalibrationString.string());
+ }
+ }
+
+ out.pressureSource = Calibration::PRESSURE_SOURCE_DEFAULT;
+ String8 pressureSourceString;
+ if (in.tryGetProperty(String8("touch.pressure.source"), pressureSourceString)) {
+ if (pressureSourceString == "pressure") {
+ out.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
+ } else if (pressureSourceString == "touch") {
+ out.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
+ } else if (pressureSourceString != "default") {
+ LOGW("Invalid value for touch.pressure.source: '%s'",
+ pressureSourceString.string());
+ }
+ }
+
+ out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"),
+ out.pressureScale);
+
+ // Size
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT;
+ String8 sizeCalibrationString;
+ if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) {
+ if (sizeCalibrationString == "none") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ } else if (sizeCalibrationString == "normalized") {
+ out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
+ } else if (sizeCalibrationString != "default") {
+ LOGW("Invalid value for touch.size.calibration: '%s'",
+ sizeCalibrationString.string());
+ }
+ }
+
+ // Orientation
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT;
+ String8 orientationCalibrationString;
+ if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) {
+ if (orientationCalibrationString == "none") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ } else if (orientationCalibrationString == "interpolated") {
+ out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ } else if (orientationCalibrationString != "default") {
+ LOGW("Invalid value for touch.orientation.calibration: '%s'",
+ orientationCalibrationString.string());
+ }
+ }
+}
+
+void TouchInputMapper::resolveCalibration() {
+ // Pressure
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_DEFAULT:
+ if (mRawAxes.pressure.valid) {
+ mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE;
+ } else if (mRawAxes.touchMajor.valid) {
+ mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH;
+ }
+ break;
+
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ if (! mRawAxes.pressure.valid) {
+ LOGW("Calibration property touch.pressure.source is 'pressure' but "
+ "the pressure axis is not available.");
+ }
+ break;
+
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ if (! mRawAxes.touchMajor.valid) {
+ LOGW("Calibration property touch.pressure.source is 'touch' but "
+ "the touchMajor axis is not available.");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_DEFAULT:
+ if (mCalibration.pressureSource != Calibration::PRESSURE_SOURCE_DEFAULT) {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE;
+ } else {
+ mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Tool Size
+ switch (mCalibration.toolSizeCalibration) {
+ case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT:
+ if (mRawAxes.toolMajor.valid) {
+ mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR;
+ } else {
+ mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Touch Size
+ switch (mCalibration.touchSizeCalibration) {
+ case Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT:
+ if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE
+ && mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) {
+ mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE;
+ } else {
+ mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Size
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_DEFAULT:
+ if (mRawAxes.toolMajor.valid) {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED;
+ } else {
+ mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Orientation
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_DEFAULT:
+ if (mRawAxes.orientation.valid) {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED;
+ } else {
+ mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void TouchInputMapper::dumpCalibration(String8& dump) {
+ dump.append(INDENT3 "Calibration:\n");
+
+ // Touch Size
+ switch (mCalibration.touchSizeCalibration) {
+ case Calibration::TOUCH_SIZE_CALIBRATION_NONE:
+ dump.append(INDENT4 "touch.touchSize.calibration: none\n");
+ break;
+ case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
+ dump.append(INDENT4 "touch.touchSize.calibration: geometric\n");
+ break;
+ case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
+ dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
+ break;
+ default:
+ assert(false);
+ }
+
+ // Tool Size
+ switch (mCalibration.toolSizeCalibration) {
+ case Calibration::TOOL_SIZE_CALIBRATION_NONE:
+ dump.append(INDENT4 "touch.toolSize.calibration: none\n");
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
+ dump.append(INDENT4 "touch.toolSize.calibration: geometric\n");
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
+ dump.append(INDENT4 "touch.toolSize.calibration: linear\n");
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_AREA:
+ dump.append(INDENT4 "touch.toolSize.calibration: area\n");
+ break;
+ default:
+ assert(false);
+ }
+
+ if (mCalibration.haveToolSizeLinearScale) {
+ dump.appendFormat(INDENT4 "touch.toolSize.linearScale: %0.3f\n",
+ mCalibration.toolSizeLinearScale);
+ }
+
+ if (mCalibration.haveToolSizeLinearBias) {
+ dump.appendFormat(INDENT4 "touch.toolSize.linearBias: %0.3f\n",
+ mCalibration.toolSizeLinearBias);
+ }
+
+ if (mCalibration.haveToolSizeAreaScale) {
+ dump.appendFormat(INDENT4 "touch.toolSize.areaScale: %0.3f\n",
+ mCalibration.toolSizeAreaScale);
+ }
+
+ if (mCalibration.haveToolSizeAreaBias) {
+ dump.appendFormat(INDENT4 "touch.toolSize.areaBias: %0.3f\n",
+ mCalibration.toolSizeAreaBias);
+ }
+
+ if (mCalibration.haveToolSizeIsSummed) {
+ dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n",
+ mCalibration.toolSizeIsSummed);
+ }
+
+ // Pressure
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_NONE:
+ dump.append(INDENT4 "touch.pressure.calibration: none\n");
+ break;
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ dump.append(INDENT4 "touch.pressure.calibration: physical\n");
+ break;
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
+ break;
+ default:
+ assert(false);
+ }
+
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ dump.append(INDENT4 "touch.pressure.source: pressure\n");
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ dump.append(INDENT4 "touch.pressure.source: touch\n");
+ break;
+ case Calibration::PRESSURE_SOURCE_DEFAULT:
+ break;
+ default:
+ assert(false);
+ }
+
+ if (mCalibration.havePressureScale) {
+ dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n",
+ mCalibration.pressureScale);
+ }
+
+ // Size
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NONE:
+ dump.append(INDENT4 "touch.size.calibration: none\n");
+ break;
+ case Calibration::SIZE_CALIBRATION_NORMALIZED:
+ dump.append(INDENT4 "touch.size.calibration: normalized\n");
+ break;
+ default:
+ assert(false);
+ }
+
+ // Orientation
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_NONE:
+ dump.append(INDENT4 "touch.orientation.calibration: none\n");
+ break;
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ dump.append(INDENT4 "touch.orientation.calibration: interpolated\n");
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void TouchInputMapper::reset() {
+ // Synthesize touch up event if touch is currently down.
+ // This will also take care of finishing virtual key processing if needed.
+ if (mLastTouch.pointerCount != 0) {
+ nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
+ mCurrentTouch.clear();
+ syncTouch(when, true);
+ }
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+ initializeLocked();
+ } // release lock
+
+ InputMapper::reset();
+}
+
+void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) {
+ uint32_t policyFlags = 0;
+
+ // Preprocess pointer data.
+
+ if (mParameters.useBadTouchFilter) {
+ if (applyBadTouchFilter()) {
+ havePointerIds = false;
+ }
+ }
+
+ if (mParameters.useJumpyTouchFilter) {
+ if (applyJumpyTouchFilter()) {
+ havePointerIds = false;
+ }
+ }
+
+ if (! havePointerIds) {
+ calculatePointerIds();
+ }
+
+ TouchData temp;
+ TouchData* savedTouch;
+ if (mParameters.useAveragingTouchFilter) {
+ temp.copyFrom(mCurrentTouch);
+ savedTouch = & temp;
+
+ applyAveragingTouchFilter();
+ } else {
+ savedTouch = & mCurrentTouch;
+ }
+
+ // Process touches and virtual keys.
+
+ TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
+ if (touchResult == DISPATCH_TOUCH) {
+ dispatchTouches(when, policyFlags);
+ }
+
+ // Copy current touch to last touch in preparation for the next cycle.
+
+ if (touchResult == DROP_STROKE) {
+ mLastTouch.clear();
+ } else {
+ mLastTouch.copyFrom(*savedTouch);
+ }
+}
+
+TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
+ nsecs_t when, uint32_t policyFlags) {
+ int32_t keyEventAction, keyEventFlags;
+ int32_t keyCode, scanCode, downTime;
+ TouchResult touchResult;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Update surface size and orientation, including virtual key positions.
+ if (! configureSurfaceLocked()) {
+ return DROP_STROKE;
+ }
+
+ // Check for virtual key press.
+ if (mLocked.currentVirtualKey.down) {
+ if (mCurrentTouch.pointerCount == 0) {
+ // Pointer went up while virtual key was down.
+ mLocked.currentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
+
+ if (mCurrentTouch.pointerCount == 1) {
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
+ if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) {
+ // Pointer is still within the space of the virtual key.
+ return SKIP_TOUCH;
+ }
+ }
+
+ // Pointer left virtual key area or another pointer also went down.
+ // Send key cancellation and drop the stroke so subsequent motions will be
+ // considered fresh downs. This is useful when the user swipes away from the
+ // virtual key area into the main display surface.
+ mLocked.currentVirtualKey.down = false;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_UP;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | AKEY_EVENT_FLAG_CANCELED;
+
+ // Check whether the pointer moved inside the display area where we should
+ // start a new stroke.
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ if (isPointInsideSurfaceLocked(x, y)) {
+ mLastTouch.clear();
+ touchResult = DISPATCH_TOUCH;
+ } else {
+ touchResult = DROP_STROKE;
+ }
+ } else {
+ if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
+ // Pointer just went down. Handle off-screen touches, if needed.
+ int32_t x = mCurrentTouch.pointers[0].x;
+ int32_t y = mCurrentTouch.pointers[0].y;
+ if (! isPointInsideSurfaceLocked(x, y)) {
+ // If exactly one pointer went down, check for virtual key hit.
+ // Otherwise we will drop the entire stroke.
+ if (mCurrentTouch.pointerCount == 1) {
+ const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y);
+ if (virtualKey) {
+ mLocked.currentVirtualKey.down = true;
+ mLocked.currentVirtualKey.downTime = when;
+ mLocked.currentVirtualKey.keyCode = virtualKey->keyCode;
+ mLocked.currentVirtualKey.scanCode = virtualKey->scanCode;
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mLocked.currentVirtualKey.keyCode,
+ mLocked.currentVirtualKey.scanCode);
+#endif
+ keyEventAction = AKEY_EVENT_ACTION_DOWN;
+ keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
+ | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
+ touchResult = SKIP_TOUCH;
+ goto DispatchVirtualKey;
+ }
+ }
+ return DROP_STROKE;
+ }
+ }
+ return DISPATCH_TOUCH;
+ }
+
+ DispatchVirtualKey:
+ // Collect remaining state needed to dispatch virtual key.
+ keyCode = mLocked.currentVirtualKey.keyCode;
+ scanCode = mLocked.currentVirtualKey.scanCode;
+ downTime = mLocked.currentVirtualKey.downTime;
+ } // release lock
+
+ // Dispatch virtual key.
+ int32_t metaState = mContext->getGlobalMetaState();
+ policyFlags |= POLICY_FLAG_VIRTUAL;
+ getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ return touchResult;
+}
+
+void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
+ if (currentPointerCount == 0 && lastPointerCount == 0) {
+ return; // nothing to do!
+ }
+
+ BitSet32 currentIdBits = mCurrentTouch.idBits;
+ BitSet32 lastIdBits = mLastTouch.idBits;
+
+ if (currentIdBits == lastIdBits) {
+ // No pointer id changes so this is a move event.
+ // The dispatcher takes care of batching moves so we don't have to deal with that here.
+ int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE;
+ dispatchTouch(when, policyFlags, & mCurrentTouch,
+ currentIdBits, -1, currentPointerCount, motionEventAction);
+ } else {
+ // There may be pointers going up and pointers going down and pointers moving
+ // all at the same time.
+ BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
+ BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
+ BitSet32 activeIdBits(lastIdBits.value);
+ uint32_t pointerCount = lastPointerCount;
+
+ // Produce an intermediate representation of the touch data that consists of the
+ // old location of pointers that have just gone up and the new location of pointers that
+ // have just moved but omits the location of pointers that have just gone down.
+ TouchData interimTouch;
+ interimTouch.copyFrom(mLastTouch);
+
+ BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
+ bool moveNeeded = false;
+ while (!moveIdBits.isEmpty()) {
+ uint32_t moveId = moveIdBits.firstMarkedBit();
+ moveIdBits.clearBit(moveId);
+
+ int32_t oldIndex = mLastTouch.idToIndex[moveId];
+ int32_t newIndex = mCurrentTouch.idToIndex[moveId];
+ if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) {
+ interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex];
+ moveNeeded = true;
+ }
+ }
+
+ // Dispatch pointer up events using the interim pointer locations.
+ while (!upIdBits.isEmpty()) {
+ uint32_t upId = upIdBits.firstMarkedBit();
+ upIdBits.clearBit(upId);
+ BitSet32 oldActiveIdBits = activeIdBits;
+ activeIdBits.clearBit(upId);
+
+ int32_t motionEventAction;
+ if (activeIdBits.isEmpty()) {
+ motionEventAction = AMOTION_EVENT_ACTION_UP;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
+ }
+
+ dispatchTouch(when, policyFlags, &interimTouch,
+ oldActiveIdBits, upId, pointerCount, motionEventAction);
+ pointerCount -= 1;
+ }
+
+ // Dispatch move events if any of the remaining pointers moved from their old locations.
+ // Although applications receive new locations as part of individual pointer up
+ // events, they do not generally handle them except when presented in a move event.
+ if (moveNeeded) {
+ dispatchTouch(when, policyFlags, &mCurrentTouch,
+ activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE);
+ }
+
+ // Dispatch pointer down events using the new pointer locations.
+ while (!downIdBits.isEmpty()) {
+ uint32_t downId = downIdBits.firstMarkedBit();
+ downIdBits.clearBit(downId);
+ BitSet32 oldActiveIdBits = activeIdBits;
+ activeIdBits.markBit(downId);
+
+ int32_t motionEventAction;
+ if (oldActiveIdBits.isEmpty()) {
+ motionEventAction = AMOTION_EVENT_ACTION_DOWN;
+ mDownTime = when;
+ } else {
+ motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN;
+ }
+
+ pointerCount += 1;
+ dispatchTouch(when, policyFlags, &mCurrentTouch,
+ activeIdBits, downId, pointerCount, motionEventAction);
+ }
+ }
+}
+
+void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags,
+ TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount,
+ int32_t motionEventAction) {
+ int32_t pointerIds[MAX_POINTERS];
+ PointerCoords pointerCoords[MAX_POINTERS];
+ int32_t motionEventEdgeFlags = 0;
+ float xPrecision, yPrecision;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ // Walk through the the active pointers and map touch screen coordinates (TouchData) into
+ // display coordinates (PointerCoords) and adjust for display orientation.
+ for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ uint32_t inIndex = touch->idToIndex[id];
+
+ const PointerData& in = touch->pointers[inIndex];
+
+ // X and Y
+ float x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
+ float y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
+
+ // ToolMajor and ToolMinor
+ float toolMajor, toolMinor;
+ switch (mCalibration.toolSizeCalibration) {
+ case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC:
+ toolMajor = in.toolMajor * mLocked.geometricScale;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor * mLocked.geometricScale;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_LINEAR:
+ toolMajor = in.toolMajor != 0
+ ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias
+ : 0;
+ if (mRawAxes.toolMinor.valid) {
+ toolMinor = in.toolMinor != 0
+ ? in.toolMinor * mLocked.toolSizeLinearScale
+ + mLocked.toolSizeLinearBias
+ : 0;
+ } else {
+ toolMinor = toolMajor;
+ }
+ break;
+ case Calibration::TOOL_SIZE_CALIBRATION_AREA:
+ if (in.toolMajor != 0) {
+ float diameter = sqrtf(in.toolMajor
+ * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias);
+ toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias;
+ } else {
+ toolMajor = 0;
+ }
+ toolMinor = toolMajor;
+ break;
+ default:
+ toolMajor = 0;
+ toolMinor = 0;
+ break;
+ }
+
+ if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) {
+ toolMajor /= pointerCount;
+ toolMinor /= pointerCount;
+ }
+
+ // Pressure
+ float rawPressure;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ rawPressure = in.pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ rawPressure = in.touchMajor;
+ break;
+ default:
+ rawPressure = 0;
+ }
+
+ float pressure;
+ switch (mCalibration.pressureCalibration) {
+ case Calibration::PRESSURE_CALIBRATION_PHYSICAL:
+ case Calibration::PRESSURE_CALIBRATION_AMPLITUDE:
+ pressure = rawPressure * mLocked.pressureScale;
+ break;
+ default:
+ pressure = 1;
+ break;
+ }
+
+ // TouchMajor and TouchMinor
+ float touchMajor, touchMinor;
+ switch (mCalibration.touchSizeCalibration) {
+ case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC:
+ touchMajor = in.touchMajor * mLocked.geometricScale;
+ if (mRawAxes.touchMinor.valid) {
+ touchMinor = in.touchMinor * mLocked.geometricScale;
+ } else {
+ touchMinor = touchMajor;
+ }
+ break;
+ case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE:
+ touchMajor = toolMajor * pressure;
+ touchMinor = toolMinor * pressure;
+ break;
+ default:
+ touchMajor = 0;
+ touchMinor = 0;
+ break;
+ }
+
+ if (touchMajor > toolMajor) {
+ touchMajor = toolMajor;
+ }
+ if (touchMinor > toolMinor) {
+ touchMinor = toolMinor;
+ }
+
+ // Size
+ float size;
+ switch (mCalibration.sizeCalibration) {
+ case Calibration::SIZE_CALIBRATION_NORMALIZED: {
+ float rawSize = mRawAxes.toolMinor.valid
+ ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
+ size = rawSize * mLocked.sizeScale;
+ break;
+ }
+ default:
+ size = 0;
+ break;
+ }
+
+ // Orientation
+ float orientation;
+ switch (mCalibration.orientationCalibration) {
+ case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED:
+ orientation = in.orientation * mLocked.orientationScale;
+ break;
+ default:
+ orientation = 0;
+ }
+
+ // Adjust coords for orientation.
+ switch (mLocked.surfaceOrientation) {
+ case InputReaderPolicyInterface::ROTATION_90: {
+ float xTemp = x;
+ x = y;
+ y = mLocked.surfaceWidth - xTemp;
+ orientation -= M_PI_2;
+ if (orientation < - M_PI_2) {
+ orientation += M_PI;
+ }
+ break;
+ }
+ case InputReaderPolicyInterface::ROTATION_180: {
+ x = mLocked.surfaceWidth - x;
+ y = mLocked.surfaceHeight - y;
+ orientation = - orientation;
+ break;
+ }
+ case InputReaderPolicyInterface::ROTATION_270: {
+ float xTemp = x;
+ x = mLocked.surfaceHeight - y;
+ y = xTemp;
+ orientation += M_PI_2;
+ if (orientation > M_PI_2) {
+ orientation -= M_PI;
+ }
+ break;
+ }
+ }
+
+ // Write output coords.
+ PointerCoords& out = pointerCoords[outIndex];
+ out.x = x;
+ out.y = y;
+ out.pressure = pressure;
+ out.size = size;
+ out.touchMajor = touchMajor;
+ out.touchMinor = touchMinor;
+ out.toolMajor = toolMajor;
+ out.toolMinor = toolMinor;
+ out.orientation = orientation;
+
+ pointerIds[outIndex] = int32_t(id);
+
+ if (id == changedId) {
+ motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ }
+ }
+
+ // Check edge flags by looking only at the first pointer since the flags are
+ // global to the event.
+ if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
+ if (pointerCoords[0].x <= 0) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
+ } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
+ }
+ if (pointerCoords[0].y <= 0) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
+ } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) {
+ motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
+ }
+ }
+
+ xPrecision = mLocked.orientedXPrecision;
+ yPrecision = mLocked.orientedYPrecision;
+ } // release lock
+
+ getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
+ motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
+ pointerCount, pointerIds, pointerCoords,
+ xPrecision, yPrecision, mDownTime);
+}
+
+bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
+ if (mRawAxes.x.valid && mRawAxes.y.valid) {
+ return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
+ && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
+ }
+ return true;
+}
+
+const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked(
+ int32_t x, int32_t y) {
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+
+#if DEBUG_VIRTUAL_KEYS
+ LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y,
+ virtualKey.keyCode, virtualKey.scanCode,
+ virtualKey.hitLeft, virtualKey.hitTop,
+ virtualKey.hitRight, virtualKey.hitBottom);
+#endif
+
+ if (virtualKey.isHit(x, y)) {
+ return & virtualKey;
+ }
+ }
+
+ return NULL;
+}
+
+void TouchInputMapper::calculatePointerIds() {
+ uint32_t currentPointerCount = mCurrentTouch.pointerCount;
+ uint32_t lastPointerCount = mLastTouch.pointerCount;
+
+ if (currentPointerCount == 0) {
+ // No pointers to assign.
+ mCurrentTouch.idBits.clear();
+ } else if (lastPointerCount == 0) {
+ // All pointers are new.
+ mCurrentTouch.idBits.clear();
+ for (uint32_t i = 0; i < currentPointerCount; i++) {
+ mCurrentTouch.pointers[i].id = i;
+ mCurrentTouch.idToIndex[i] = i;
+ mCurrentTouch.idBits.markBit(i);
+ }
+ } else if (currentPointerCount == 1 && lastPointerCount == 1) {
+ // Only one pointer and no change in count so it must have the same id as before.
+ uint32_t id = mLastTouch.pointers[0].id;
+ mCurrentTouch.pointers[0].id = id;
+ mCurrentTouch.idToIndex[id] = 0;
+ mCurrentTouch.idBits.value = BitSet32::valueForBit(id);
+ } else {
+ // General case.
+ // We build a heap of squared euclidean distances between current and last pointers
+ // associated with the current and last pointer indices. Then, we find the best
+ // match (by distance) for each current pointer.
+ PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS];
+
+ uint32_t heapSize = 0;
+ for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount;
+ currentPointerIndex++) {
+ for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount;
+ lastPointerIndex++) {
+ int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x
+ - mLastTouch.pointers[lastPointerIndex].x;
+ int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y
+ - mLastTouch.pointers[lastPointerIndex].y;
+
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+ // Insert new element into the heap (sift up).
+ heap[heapSize].currentPointerIndex = currentPointerIndex;
+ heap[heapSize].lastPointerIndex = lastPointerIndex;
+ heap[heapSize].distance = distance;
+ heapSize += 1;
+ }
+ }
+
+ // Heapify
+ for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) {
+ startIndex -= 1;
+ for (uint32_t parentIndex = startIndex; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+
+ // Pull matches out by increasing order of distance.
+ // To avoid reassigning pointers that have already been matched, the loop keeps track
+ // of which last and current pointers have been matched using the matchedXXXBits variables.
+ // It also tracks the used pointer id bits.
+ BitSet32 matchedLastBits(0);
+ BitSet32 matchedCurrentBits(0);
+ BitSet32 usedIdBits(0);
+ bool first = true;
+ for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) {
+ for (;;) {
+ if (first) {
+ // The first time through the loop, we just consume the root element of
+ // the heap (the one with smallest distance).
+ first = false;
+ } else {
+ // Previous iterations consumed the root element of the heap.
+ // Pop root element off of the heap (sift down).
+ heapSize -= 1;
+ assert(heapSize > 0);
+
+ // Sift down.
+ heap[0] = heap[heapSize];
+ for (uint32_t parentIndex = 0; ;) {
+ uint32_t childIndex = parentIndex * 2 + 1;
+ if (childIndex >= heapSize) {
+ break;
+ }
+
+ if (childIndex + 1 < heapSize
+ && heap[childIndex + 1].distance < heap[childIndex].distance) {
+ childIndex += 1;
+ }
+
+ if (heap[parentIndex].distance <= heap[childIndex].distance) {
+ break;
+ }
+
+ swap(heap[parentIndex], heap[childIndex]);
+ parentIndex = childIndex;
+ }
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld",
+ i, heap[i].currentPointerIndex, heap[i].lastPointerIndex,
+ heap[i].distance);
+ }
+#endif
+ }
+
+ uint32_t currentPointerIndex = heap[0].currentPointerIndex;
+ if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched
+
+ uint32_t lastPointerIndex = heap[0].lastPointerIndex;
+ if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched
+
+ matchedCurrentBits.markBit(currentPointerIndex);
+ matchedLastBits.markBit(lastPointerIndex);
+
+ uint32_t id = mLastTouch.pointers[lastPointerIndex].id;
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld",
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+#endif
+ break;
+ }
+ }
+
+ // Assign fresh ids to new pointers.
+ if (currentPointerCount > lastPointerCount) {
+ for (uint32_t i = currentPointerCount - lastPointerCount; ;) {
+ uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit();
+ uint32_t id = usedIdBits.firstUnmarkedBit();
+
+ mCurrentTouch.pointers[currentPointerIndex].id = id;
+ mCurrentTouch.idToIndex[id] = currentPointerIndex;
+ usedIdBits.markBit(id);
+
+#if DEBUG_POINTER_ASSIGNMENT
+ LOGD("calculatePointerIds - assigned: cur=%d, id=%d",
+ currentPointerIndex, id);
+#endif
+
+ if (--i == 0) break; // done
+ matchedCurrentBits.markBit(currentPointerIndex);
+ }
+ }
+
+ // Fix id bits.
+ mCurrentTouch.idBits = usedIdBits;
+ }
+}
+
+/* Special hack for devices that have bad screen data: if one of the
+ * points has moved more than a screen height from the last position,
+ * then drop it. */
+bool TouchInputMapper::applyBadTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mRawAxes.y.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+
+ // Nothing to do if there are no points.
+ if (pointerCount == 0) {
+ return false;
+ }
+
+ // Don't do anything if a finger is going down or up. We run
+ // here before assigning pointer IDs, so there isn't a good
+ // way to do per-finger matching.
+ if (pointerCount != mLastTouch.pointerCount) {
+ return false;
+ }
+
+ // We consider a single movement across more than a 7/16 of
+ // the long size of the screen to be bad. This was a magic value
+ // determined by looking at the maximum distance it is feasible
+ // to actually move in one sample.
+ int32_t maxDeltaY = mRawAxes.y.getRange() * 7 / 16;
+
+ // XXX The original code in InputDevice.java included commented out
+ // code for testing the X axis. Note that when we drop a point
+ // we don't actually restore the old X either. Strange.
+ // The old code also tries to track when bad points were previously
+ // detected but it turns out that due to the placement of a "break"
+ // at the end of the loop, we never set mDroppedBadPoint to true
+ // so it is effectively dead code.
+ // Need to figure out if the old code is busted or just overcomplicated
+ // but working as intended.
+
+ // Look through all new points and see if any are farther than
+ // acceptable from all previous points.
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t y = mCurrentTouch.pointers[i].y;
+ int32_t closestY = INT_MAX;
+ int32_t closestDeltaY = 0;
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
+#endif
+
+ for (uint32_t j = pointerCount; j-- > 0; ) {
+ int32_t lastY = mLastTouch.pointers[j].y;
+ int32_t deltaY = abs(y - lastY);
+
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
+ j, lastY, deltaY);
+#endif
+
+ if (deltaY < maxDeltaY) {
+ goto SkipSufficientlyClosePoint;
+ }
+ if (deltaY < closestDeltaY) {
+ closestDeltaY = deltaY;
+ closestY = lastY;
+ }
+ }
+
+ // Must not have found a close enough match.
+#if DEBUG_HACKS
+ LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
+ i, y, closestY, closestDeltaY, maxDeltaY);
+#endif
+
+ mCurrentTouch.pointers[i].y = closestY;
+ return true; // XXX original code only corrects one point
+
+ SkipSufficientlyClosePoint: ;
+ }
+
+ // No change.
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: drop points where
+ * the coordinate value for one axis has jumped to the other pointer's location.
+ */
+bool TouchInputMapper::applyJumpyTouchFilter() {
+ // This hack requires valid axis parameters.
+ if (! mRawAxes.y.valid) {
+ return false;
+ }
+
+ uint32_t pointerCount = mCurrentTouch.pointerCount;
+ if (mLastTouch.pointerCount != pointerCount) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
+ mLastTouch.pointerCount, pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ LOGD(" Pointer %d (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
+
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+ if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
+ // Just drop the first few events going from 1 to 2 pointers.
+ // They're bad often enough that they're not worth considering.
+ mCurrentTouch.pointerCount = 1;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Pointer 2 dropped");
+#endif
+ return true;
+ } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
+ // The event when we go from 2 -> 1 tends to be messed up too
+ mCurrentTouch.pointerCount = 2;
+ mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
+ mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+
+#if DEBUG_HACKS
+ for (int32_t i = 0; i < 2; i++) {
+ LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
+ mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
+ }
+#endif
+ return true;
+ }
+ }
+ // Reset jumpy points dropped on other transitions or if limit exceeded.
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Transition - drop limit reset");
+#endif
+ return false;
+ }
+
+ // We have the same number of pointers as last time.
+ // A 'jumpy' point is one where the coordinate value for one axis
+ // has jumped to the other pointer's location. No need to do anything
+ // else if we only have one pointer.
+ if (pointerCount < 2) {
+ return false;
+ }
+
+ if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
+ int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
+
+ // We only replace the single worst jumpy point as characterized by pointer distance
+ // in a single axis.
+ int32_t badPointerIndex = -1;
+ int32_t badPointerReplacementIndex = -1;
+ int32_t badPointerDistance = INT_MIN; // distance to be corrected
+
+ for (uint32_t i = pointerCount; i-- > 0; ) {
+ int32_t x = mCurrentTouch.pointers[i].x;
+ int32_t y = mCurrentTouch.pointers[i].y;
+
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
+#endif
+
+ // Check if a touch point is too close to another's coordinates
+ bool dropX = false, dropY = false;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ if (i == j) {
+ continue;
+ }
+
+ if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
+ dropX = true;
+ break;
+ }
+
+ if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
+ dropY = true;
+ break;
+ }
+ }
+ if (! dropX && ! dropY) {
+ continue; // not jumpy
+ }
+
+ // Find a replacement candidate by comparing with older points on the
+ // complementary (non-jumpy) axis.
+ int32_t distance = INT_MIN; // distance to be corrected
+ int32_t replacementIndex = -1;
+
+ if (dropX) {
+ // X looks too close. Find an older replacement point with a close Y.
+ int32_t smallestDeltaY = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
+ if (deltaY < smallestDeltaY) {
+ smallestDeltaY = deltaY;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(x - mLastTouch.pointers[replacementIndex].x);
+ } else {
+ // Y looks too close. Find an older replacement point with a close X.
+ int32_t smallestDeltaX = INT_MAX;
+ for (uint32_t j = 0; j < pointerCount; j++) {
+ int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
+ if (deltaX < smallestDeltaX) {
+ smallestDeltaX = deltaX;
+ replacementIndex = j;
+ }
+ }
+ distance = abs(y - mLastTouch.pointers[replacementIndex].y);
+ }
+
+ // If replacing this pointer would correct a worse error than the previous ones
+ // considered, then use this replacement instead.
+ if (distance > badPointerDistance) {
+ badPointerIndex = i;
+ badPointerReplacementIndex = replacementIndex;
+ badPointerDistance = distance;
+ }
+ }
+
+ // Correct the jumpy pointer if one was found.
+ if (badPointerIndex >= 0) {
+#if DEBUG_HACKS
+ LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
+ badPointerIndex,
+ mLastTouch.pointers[badPointerReplacementIndex].x,
+ mLastTouch.pointers[badPointerReplacementIndex].y);
+#endif
+
+ mCurrentTouch.pointers[badPointerIndex].x =
+ mLastTouch.pointers[badPointerReplacementIndex].x;
+ mCurrentTouch.pointers[badPointerIndex].y =
+ mLastTouch.pointers[badPointerReplacementIndex].y;
+ mJumpyTouchFilter.jumpyPointsDropped += 1;
+ return true;
+ }
+ }
+
+ mJumpyTouchFilter.jumpyPointsDropped = 0;
+ return false;
+}
+
+/* Special hack for devices that have bad screen data: aggregate and
+ * compute averages of the coordinate data, to reduce the amount of
+ * jitter seen by applications. */
+void TouchInputMapper::applyAveragingTouchFilter() {
+ for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
+ uint32_t id = mCurrentTouch.pointers[currentIndex].id;
+ int32_t x = mCurrentTouch.pointers[currentIndex].x;
+ int32_t y = mCurrentTouch.pointers[currentIndex].y;
+ int32_t pressure;
+ switch (mCalibration.pressureSource) {
+ case Calibration::PRESSURE_SOURCE_PRESSURE:
+ pressure = mCurrentTouch.pointers[currentIndex].pressure;
+ break;
+ case Calibration::PRESSURE_SOURCE_TOUCH:
+ pressure = mCurrentTouch.pointers[currentIndex].touchMajor;
+ break;
+ default:
+ pressure = 1;
+ break;
+ }
+
+ if (mLastTouch.idBits.hasBit(id)) {
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
+ uint32_t start = mAveragingTouchFilter.historyStart[id];
+ uint32_t end = mAveragingTouchFilter.historyEnd[id];
+
+ int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
+ int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
+ uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
+ id, distance);
+#endif
+
+ if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
+ end += 1;
+ if (end > AVERAGING_HISTORY_SIZE) {
+ end = 0;
+ }
+
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
+ if (end == start) {
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ // Add the raw data to the historical trace.
+ mAveragingTouchFilter.historyStart[id] = start;
+ mAveragingTouchFilter.historyEnd[id] = end;
+ mAveragingTouchFilter.historyData[end].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[end].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+
+ // Average over all historical positions in the trace by total pressure.
+ int32_t averagedX = 0;
+ int32_t averagedY = 0;
+ int32_t totalPressure = 0;
+ for (;;) {
+ int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
+ int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
+ .pointers[id].pressure;
+
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
+ totalPressure += historicalPressure;
+
+ if (start == end) {
+ break;
+ }
+
+ start += 1;
+ if (start > AVERAGING_HISTORY_SIZE) {
+ start = 0;
+ }
+ }
+
+ if (totalPressure != 0) {
+ averagedX /= totalPressure;
+ averagedY /= totalPressure;
+
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - "
+ "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
+ averagedX, averagedY);
+#endif
+
+ mCurrentTouch.pointers[currentIndex].x = averagedX;
+ mCurrentTouch.pointers[currentIndex].y = averagedY;
+ }
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
+#endif
+ }
+ } else {
+#if DEBUG_HACKS
+ LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
+#endif
+ }
+
+ // Reset pointer history.
+ mAveragingTouchFilter.historyStart[id] = 0;
+ mAveragingTouchFilter.historyEnd[id] = 0;
+ mAveragingTouchFilter.historyData[0].pointers[id].x = x;
+ mAveragingTouchFilter.historyData[0].pointers[id].y = y;
+ mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
+ }
+}
+
+int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+ if (virtualKey.keyCode == keyCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release lock
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) {
+ return AKEY_STATE_VIRTUAL;
+ }
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+ if (virtualKey.scanCode == scanCode) {
+ return AKEY_STATE_UP;
+ }
+ }
+ } // release lock
+
+ return AKEY_STATE_UNKNOWN;
+}
+
+bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ size_t numVirtualKeys = mLocked.virtualKeys.size();
+ for (size_t i = 0; i < numVirtualKeys; i++) {
+ const VirtualKey& virtualKey = mLocked.virtualKeys[i];
+
+ for (size_t i = 0; i < numCodes; i++) {
+ if (virtualKey.keyCode == keyCodes[i]) {
+ outFlags[i] = 1;
+ }
+ }
+ }
+ } // release lock
+
+ return true;
+}
+
+
+// --- SingleTouchInputMapper ---
+
+SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
+}
+
+SingleTouchInputMapper::~SingleTouchInputMapper() {
+}
+
+void SingleTouchInputMapper::initialize() {
+ mAccumulator.clear();
+
+ mDown = false;
+ mX = 0;
+ mY = 0;
+ mPressure = 0; // default to 0 for devices that don't report pressure
+ mToolWidth = 0; // default to 0 for devices that don't report tool width
+}
+
+void SingleTouchInputMapper::reset() {
+ TouchInputMapper::reset();
+
+ initialize();
+ }
+
+void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_KEY:
+ switch (rawEvent->scanCode) {
+ case BTN_TOUCH:
+ mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
+ mAccumulator.btnTouch = rawEvent->value != 0;
+ // Don't sync immediately. Wait until the next SYN_REPORT since we might
+ // not have received valid position information yet. This logic assumes that
+ // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
+ break;
+ }
+ break;
+
+ case EV_ABS:
+ switch (rawEvent->scanCode) {
+ case ABS_X:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_X;
+ mAccumulator.absX = rawEvent->value;
+ break;
+ case ABS_Y:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_Y;
+ mAccumulator.absY = rawEvent->value;
+ break;
+ case ABS_PRESSURE:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE;
+ mAccumulator.absPressure = rawEvent->value;
+ break;
+ case ABS_TOOL_WIDTH:
+ mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH;
+ mAccumulator.absToolWidth = rawEvent->value;
+ break;
+ }
+ break;
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void SingleTouchInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
+ if (fields & Accumulator::FIELD_BTN_TOUCH) {
+ mDown = mAccumulator.btnTouch;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_X) {
+ mX = mAccumulator.absX;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_Y) {
+ mY = mAccumulator.absY;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_PRESSURE) {
+ mPressure = mAccumulator.absPressure;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) {
+ mToolWidth = mAccumulator.absToolWidth;
+ }
+
+ mCurrentTouch.clear();
+
+ if (mDown) {
+ mCurrentTouch.pointerCount = 1;
+ mCurrentTouch.pointers[0].id = 0;
+ mCurrentTouch.pointers[0].x = mX;
+ mCurrentTouch.pointers[0].y = mY;
+ mCurrentTouch.pointers[0].pressure = mPressure;
+ mCurrentTouch.pointers[0].touchMajor = 0;
+ mCurrentTouch.pointers[0].touchMinor = 0;
+ mCurrentTouch.pointers[0].toolMajor = mToolWidth;
+ mCurrentTouch.pointers[0].toolMinor = mToolWidth;
+ mCurrentTouch.pointers[0].orientation = 0;
+ mCurrentTouch.idToIndex[0] = 0;
+ mCurrentTouch.idBits.markBit(0);
+ }
+
+ syncTouch(when, true);
+
+ mAccumulator.clear();
+}
+
+void SingleTouchInputMapper::configureRawAxes() {
+ TouchInputMapper::configureRawAxes();
+
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor);
+}
+
+
+// --- MultiTouchInputMapper ---
+
+MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) :
+ TouchInputMapper(device, associatedDisplayId) {
+ initialize();
+}
+
+MultiTouchInputMapper::~MultiTouchInputMapper() {
+}
+
+void MultiTouchInputMapper::initialize() {
+ mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::reset() {
+ TouchInputMapper::reset();
+
+ initialize();
+}
+
+void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+ switch (rawEvent->type) {
+ case EV_ABS: {
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+ Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
+
+ switch (rawEvent->scanCode) {
+ case ABS_MT_POSITION_X:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
+ pointer->absMTPositionX = rawEvent->value;
+ break;
+ case ABS_MT_POSITION_Y:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
+ pointer->absMTPositionY = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
+ pointer->absMTTouchMajor = rawEvent->value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
+ pointer->absMTTouchMinor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+ pointer->absMTWidthMajor = rawEvent->value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
+ pointer->absMTWidthMinor = rawEvent->value;
+ break;
+ case ABS_MT_ORIENTATION:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
+ pointer->absMTOrientation = rawEvent->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
+ pointer->absMTTrackingId = rawEvent->value;
+ break;
+ case ABS_MT_PRESSURE:
+ pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE;
+ pointer->absMTPressure = rawEvent->value;
+ break;
+ }
+ break;
+ }
+
+ case EV_SYN:
+ switch (rawEvent->scanCode) {
+ case SYN_MT_REPORT: {
+ // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
+ uint32_t pointerIndex = mAccumulator.pointerCount;
+
+ if (mAccumulator.pointers[pointerIndex].fields) {
+ if (pointerIndex == MAX_POINTERS) {
+ LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
+ MAX_POINTERS);
+ } else {
+ pointerIndex += 1;
+ mAccumulator.pointerCount = pointerIndex;
+ }
+ }
+
+ mAccumulator.pointers[pointerIndex].clear();
+ break;
+ }
+
+ case SYN_REPORT:
+ sync(rawEvent->when);
+ break;
+ }
+ break;
+ }
+}
+
+void MultiTouchInputMapper::sync(nsecs_t when) {
+ static const uint32_t REQUIRED_FIELDS =
+ Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y;
+
+ uint32_t inCount = mAccumulator.pointerCount;
+ uint32_t outCount = 0;
+ bool havePointerIds = true;
+
+ mCurrentTouch.clear();
+
+ for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
+ const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+ uint32_t fields = inPointer.fields;
+
+ if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
+ // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+ // Drop this finger.
+ continue;
+ }
+
+ PointerData& outPointer = mCurrentTouch.pointers[outCount];
+ outPointer.x = inPointer.absMTPositionX;
+ outPointer.y = inPointer.absMTPositionY;
+
+ if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+ if (inPointer.absMTPressure <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 pressure to indicate
+ // a pointer going up. Drop this finger.
+ continue;
+ }
+ outPointer.pressure = inPointer.absMTPressure;
+ } else {
+ // Default pressure to 0 if absent.
+ outPointer.pressure = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+ if (inPointer.absMTTouchMajor <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+ // a pointer going up. Drop this finger.
+ continue;
+ }
+ outPointer.touchMajor = inPointer.absMTTouchMajor;
+ } else {
+ // Default touch area to 0 if absent.
+ outPointer.touchMajor = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+ outPointer.touchMinor = inPointer.absMTTouchMinor;
+ } else {
+ // Assume touch area is circular.
+ outPointer.touchMinor = outPointer.touchMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+ outPointer.toolMajor = inPointer.absMTWidthMajor;
+ } else {
+ // Default tool area to 0 if absent.
+ outPointer.toolMajor = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+ outPointer.toolMinor = inPointer.absMTWidthMinor;
+ } else {
+ // Assume tool area is circular.
+ outPointer.toolMinor = outPointer.toolMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+ outPointer.orientation = inPointer.absMTOrientation;
+ } else {
+ // Default orientation to vertical if absent.
+ outPointer.orientation = 0;
+ }
+
+ // Assign pointer id using tracking id if available.
+ if (havePointerIds) {
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(inPointer.absMTTrackingId);
+
+ if (id > MAX_POINTER_ID) {
+#if DEBUG_POINTERS
+ LOGD("Pointers: Ignoring driver provided pointer id %d because "
+ "it is larger than max supported id %d",
+ id, MAX_POINTER_ID);
+#endif
+ havePointerIds = false;
+ }
+ else {
+ outPointer.id = id;
+ mCurrentTouch.idToIndex[id] = outCount;
+ mCurrentTouch.idBits.markBit(id);
+ }
+ } else {
+ havePointerIds = false;
+ }
+ }
+
+ outCount += 1;
+ }
+
+ mCurrentTouch.pointerCount = outCount;
+
+ syncTouch(when, havePointerIds);
+
+ mAccumulator.clear();
+}
+
+void MultiTouchInputMapper::configureRawAxes() {
+ TouchInputMapper::configureRawAxes();
+
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure);
+}
+
+
+} // namespace android
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
new file mode 100644
index 0000000..2c6346e
--- /dev/null
+++ b/libs/ui/InputTransport.cpp
@@ -0,0 +1,702 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Provides a shared memory transport for input events.
+//
+#define LOG_TAG "InputTransport"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about channel signalling (send signal, receive signal)
+#define DEBUG_CHANNEL_SIGNALS 0
+
+// Log debug messages whenever InputChannel objects are created/destroyed
+#define DEBUG_CHANNEL_LIFECYCLE 0
+
+// Log debug messages about transport actions (initialize, reset, publish, ...)
+#define DEBUG_TRANSPORT_ACTIONS 0
+
+
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <ui/InputTransport.h>
+#include <unistd.h>
+
+namespace android {
+
+// Must be at least sizeof(InputMessage) + sufficient space for pointer data
+static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
+
+// Signal sent by the producer to the consumer to inform it that a new message is
+// available to be consumed in the shared memory buffer.
+static const char INPUT_SIGNAL_DISPATCH = 'D';
+
+// Signal sent by the consumer to the producer to inform it that it has finished
+// consuming the most recent message.
+static const char INPUT_SIGNAL_FINISHED = 'f';
+
+
+// --- InputChannel ---
+
+InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
+ int32_t sendPipeFd) :
+ mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
+#if DEBUG_CHANNEL_LIFECYCLE
+ LOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
+ mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
+#endif
+
+ int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe "
+ "non-blocking. errno=%d", mName.string(), errno);
+
+ result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe "
+ "non-blocking. errno=%d", mName.string(), errno);
+}
+
+InputChannel::~InputChannel() {
+#if DEBUG_CHANNEL_LIFECYCLE
+ LOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
+ mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
+#endif
+
+ ::close(mAshmemFd);
+ ::close(mReceivePipeFd);
+ ::close(mSendPipeFd);
+}
+
+status_t InputChannel::openInputChannelPair(const String8& name,
+ sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
+ status_t result;
+
+ int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
+ if (serverAshmemFd < 0) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
+ name.string(), errno);
+ } else {
+ result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
+ if (result < 0) {
+ LOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",
+ name.string(), result, serverAshmemFd);
+ } else {
+ // Dup the file descriptor because the server and client input channel objects that
+ // are returned may have different lifetimes but they share the same shared memory region.
+ int clientAshmemFd;
+ clientAshmemFd = dup(serverAshmemFd);
+ if (clientAshmemFd < 0) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
+ name.string(), errno);
+ } else {
+ int forward[2];
+ if (pipe(forward)) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not create forward pipe. errno=%d",
+ name.string(), errno);
+ } else {
+ int reverse[2];
+ if (pipe(reverse)) {
+ result = -errno;
+ LOGE("channel '%s' ~ Could not create reverse pipe. errno=%d",
+ name.string(), errno);
+ } else {
+ String8 serverChannelName = name;
+ serverChannelName.append(" (server)");
+ outServerChannel = new InputChannel(serverChannelName,
+ serverAshmemFd, reverse[0], forward[1]);
+
+ String8 clientChannelName = name;
+ clientChannelName.append(" (client)");
+ outClientChannel = new InputChannel(clientChannelName,
+ clientAshmemFd, forward[0], reverse[1]);
+ return OK;
+ }
+ ::close(forward[0]);
+ ::close(forward[1]);
+ }
+ ::close(clientAshmemFd);
+ }
+ }
+ ::close(serverAshmemFd);
+ }
+
+ outServerChannel.clear();
+ outClientChannel.clear();
+ return result;
+}
+
+status_t InputChannel::sendSignal(char signal) {
+ ssize_t nWrite;
+ do {
+ nWrite = ::write(mSendPipeFd, & signal, 1);
+ } while (nWrite == -1 && errno == EINTR);
+
+ if (nWrite == 1) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
+#endif
+ return OK;
+ }
+
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
+#endif
+ return -errno;
+}
+
+status_t InputChannel::receiveSignal(char* outSignal) {
+ ssize_t nRead;
+ do {
+ nRead = ::read(mReceivePipeFd, outSignal, 1);
+ } while (nRead == -1 && errno == EINTR);
+
+ if (nRead == 1) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
+#endif
+ return OK;
+ }
+
+ if (nRead == 0) { // check for EOF
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string());
+#endif
+ return DEAD_OBJECT;
+ }
+
+ if (errno == EAGAIN) {
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
+#endif
+ return WOULD_BLOCK;
+ }
+
+#if DEBUG_CHANNEL_SIGNALS
+ LOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
+#endif
+ return -errno;
+}
+
+
+// --- InputPublisher ---
+
+InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
+ mChannel(channel), mSharedMessage(NULL),
+ mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
+ mMotionEventSampleDataTail(NULL) {
+}
+
+InputPublisher::~InputPublisher() {
+ reset();
+
+ if (mSharedMessage) {
+ munmap(mSharedMessage, mAshmemSize);
+ }
+}
+
+status_t InputPublisher::initialize() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ initialize",
+ mChannel->getName().string());
+#endif
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_get_size_region(ashmemFd);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+ mAshmemSize = (size_t) result;
+
+ mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
+ if (! mSharedMessage) {
+ LOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.",
+ mChannel->getName().string(), ashmemFd);
+ return NO_MEMORY;
+ }
+
+ mPinned = true;
+ mSharedMessage->consumed = false;
+
+ return reset();
+}
+
+status_t InputPublisher::reset() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ reset",
+ mChannel->getName().string());
+#endif
+
+ if (mPinned) {
+ // Destroy the semaphore since we are about to unpin the memory region that contains it.
+ int result;
+ if (mSemaphoreInitialized) {
+ if (mSharedMessage->consumed) {
+ result = sem_post(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d in sem_post.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ result = sem_destroy(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d in sem_destroy.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+
+ mSemaphoreInitialized = false;
+ }
+
+ // Unpin the region since we no longer care about its contents.
+ int ashmemFd = mChannel->getAshmemFd();
+ result = ashmem_unpin_region(ashmemFd, 0, 0);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+
+ mPinned = false;
+ }
+
+ mMotionEventSampleDataTail = NULL;
+ mWasDispatched = false;
+ return OK;
+}
+
+status_t InputPublisher::publishInputEvent(
+ int32_t type,
+ int32_t deviceId,
+ int32_t source) {
+ if (mPinned) {
+ LOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has "
+ "not yet been reset.", mChannel->getName().string());
+ return INVALID_OPERATION;
+ }
+
+ // Pin the region.
+ // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous
+ // contents of the buffer so it does not matter whether it was purged in the meantime.
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_pin_region(ashmemFd, 0, 0);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+
+ mPinned = true;
+
+ result = sem_init(& mSharedMessage->semaphore, 1, 1);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d in sem_init.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+
+ mSemaphoreInitialized = true;
+
+ mSharedMessage->consumed = false;
+ mSharedMessage->type = type;
+ mSharedMessage->deviceId = deviceId;
+ mSharedMessage->source = source;
+ return OK;
+}
+
+status_t InputPublisher::publishKeyEvent(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t keyCode,
+ int32_t scanCode,
+ int32_t metaState,
+ int32_t repeatCount,
+ nsecs_t downTime,
+ nsecs_t eventTime) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%lld, eventTime=%lld",
+ mChannel->getName().string(),
+ deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
+ downTime, eventTime);
+#endif
+
+ status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source);
+ if (result < 0) {
+ return result;
+ }
+
+ mSharedMessage->key.action = action;
+ mSharedMessage->key.flags = flags;
+ mSharedMessage->key.keyCode = keyCode;
+ mSharedMessage->key.scanCode = scanCode;
+ mSharedMessage->key.metaState = metaState;
+ mSharedMessage->key.repeatCount = repeatCount;
+ mSharedMessage->key.downTime = downTime;
+ mSharedMessage->key.eventTime = eventTime;
+ return OK;
+}
+
+status_t InputPublisher::publishMotionEvent(
+ int32_t deviceId,
+ int32_t source,
+ int32_t action,
+ int32_t flags,
+ int32_t edgeFlags,
+ int32_t metaState,
+ float xOffset,
+ float yOffset,
+ float xPrecision,
+ float yPrecision,
+ nsecs_t downTime,
+ nsecs_t eventTime,
+ size_t pointerCount,
+ const int32_t* pointerIds,
+ const PointerCoords* pointerCoords) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, xOffset=%f, yOffset=%f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
+ "pointerCount=%d",
+ mChannel->getName().string(),
+ deviceId, source, action, flags, edgeFlags, metaState, xOffset, yOffset,
+ xPrecision, yPrecision, downTime, eventTime, pointerCount);
+#endif
+
+ if (pointerCount > MAX_POINTERS || pointerCount < 1) {
+ LOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
+ mChannel->getName().string(), pointerCount);
+ return BAD_VALUE;
+ }
+
+ status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source);
+ if (result < 0) {
+ return result;
+ }
+
+ mSharedMessage->motion.action = action;
+ mSharedMessage->motion.flags = flags;
+ mSharedMessage->motion.edgeFlags = edgeFlags;
+ mSharedMessage->motion.metaState = metaState;
+ mSharedMessage->motion.xOffset = xOffset;
+ mSharedMessage->motion.yOffset = yOffset;
+ mSharedMessage->motion.xPrecision = xPrecision;
+ mSharedMessage->motion.yPrecision = yPrecision;
+ mSharedMessage->motion.downTime = downTime;
+ mSharedMessage->motion.pointerCount = pointerCount;
+
+ mSharedMessage->motion.sampleCount = 1;
+ mSharedMessage->motion.sampleData[0].eventTime = eventTime;
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ mSharedMessage->motion.pointerIds[i] = pointerIds[i];
+ mSharedMessage->motion.sampleData[0].coords[i] = pointerCoords[i];
+ }
+
+ // Cache essential information about the motion event to ensure that a malicious consumer
+ // cannot confuse the publisher by modifying the contents of the shared memory buffer while
+ // it is being updated.
+ if (action == AMOTION_EVENT_ACTION_MOVE) {
+ mMotionEventPointerCount = pointerCount;
+ mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
+ mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
+ mSharedMessage->motion.sampleData, mMotionEventSampleDataStride);
+ } else {
+ mMotionEventSampleDataTail = NULL;
+ }
+ return OK;
+}
+
+status_t InputPublisher::appendMotionSample(
+ nsecs_t eventTime,
+ const PointerCoords* pointerCoords) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
+ mChannel->getName().string(), eventTime);
+#endif
+
+ if (! mPinned || ! mMotionEventSampleDataTail) {
+ LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
+ "AMOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string());
+ return INVALID_OPERATION;
+ }
+
+ InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement(
+ mMotionEventSampleDataTail, mMotionEventSampleDataStride);
+ size_t newBytesUsed = reinterpret_cast<char*>(newTail) -
+ reinterpret_cast<char*>(mSharedMessage);
+
+ if (newBytesUsed > mAshmemSize) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
+ "buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
+ mChannel->getName().string(),
+ mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
+#endif
+ return NO_MEMORY;
+ }
+
+ int result;
+ if (mWasDispatched) {
+ result = sem_trywait(& mSharedMessage->semaphore);
+ if (result < 0) {
+ if (errno == EAGAIN) {
+ // Only possible source of contention is the consumer having consumed (or being in the
+ // process of consuming) the message and left the semaphore count at 0.
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
+ "already been consumed.", mChannel->getName().string());
+#endif
+ return FAILED_TRANSACTION;
+ } else {
+ LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
+ mMotionEventSampleDataTail->eventTime = eventTime;
+ for (size_t i = 0; i < mMotionEventPointerCount; i++) {
+ mMotionEventSampleDataTail->coords[i] = pointerCoords[i];
+ }
+ mMotionEventSampleDataTail = newTail;
+
+ mSharedMessage->motion.sampleCount += 1;
+
+ if (mWasDispatched) {
+ result = sem_post(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("channel '%s' publisher ~ Error %d in sem_post.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+ }
+ return OK;
+}
+
+status_t InputPublisher::sendDispatchSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ sendDispatchSignal",
+ mChannel->getName().string());
+#endif
+
+ mWasDispatched = true;
+ return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
+}
+
+status_t InputPublisher::receiveFinishedSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' publisher ~ receiveFinishedSignal",
+ mChannel->getName().string());
+#endif
+
+ char signal;
+ status_t result = mChannel->receiveSignal(& signal);
+ if (result) {
+ return result;
+ }
+ if (signal != INPUT_SIGNAL_FINISHED) {
+ LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
+ mChannel->getName().string(), signal);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+// --- InputConsumer ---
+
+InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
+ mChannel(channel), mSharedMessage(NULL) {
+}
+
+InputConsumer::~InputConsumer() {
+ if (mSharedMessage) {
+ munmap(mSharedMessage, mAshmemSize);
+ }
+}
+
+status_t InputConsumer::initialize() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ initialize",
+ mChannel->getName().string());
+#endif
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_get_size_region(ashmemFd);
+ if (result < 0) {
+ LOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+
+ mAshmemSize = (size_t) result;
+
+ mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
+ if (! mSharedMessage) {
+ LOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.",
+ mChannel->getName().string(), ashmemFd);
+ return NO_MEMORY;
+ }
+
+ return OK;
+}
+
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ consume",
+ mChannel->getName().string());
+#endif
+
+ *outEvent = NULL;
+
+ int ashmemFd = mChannel->getAshmemFd();
+ int result = ashmem_pin_region(ashmemFd, 0, 0);
+ if (result != ASHMEM_NOT_PURGED) {
+ if (result == ASHMEM_WAS_PURGED) {
+ LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "
+ "which probably indicates that the publisher and consumer are out of sync.",
+ mChannel->getName().string(), result, ashmemFd);
+ return INVALID_OPERATION;
+ }
+
+ LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
+ mChannel->getName().string(), result, ashmemFd);
+ return UNKNOWN_ERROR;
+ }
+
+ if (mSharedMessage->consumed) {
+ LOGE("channel '%s' consumer ~ The current message has already been consumed.",
+ mChannel->getName().string());
+ return INVALID_OPERATION;
+ }
+
+ // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal
+ // to the publisher that the message has been consumed (or is in the process of being
+ // consumed). Eventually the publisher will reinitialize the semaphore for the next message.
+ result = sem_wait(& mSharedMessage->semaphore);
+ if (result < 0) {
+ LOGE("channel '%s' consumer ~ Error %d in sem_wait.",
+ mChannel->getName().string(), errno);
+ return UNKNOWN_ERROR;
+ }
+
+ mSharedMessage->consumed = true;
+
+ switch (mSharedMessage->type) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ KeyEvent* keyEvent = factory->createKeyEvent();
+ if (! keyEvent) return NO_MEMORY;
+
+ populateKeyEvent(keyEvent);
+
+ *outEvent = keyEvent;
+ break;
+ }
+
+ case AINPUT_EVENT_TYPE_MOTION: {
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (! motionEvent) return NO_MEMORY;
+
+ populateMotionEvent(motionEvent);
+
+ *outEvent = motionEvent;
+ break;
+ }
+
+ default:
+ LOGE("channel '%s' consumer ~ Received message of unknown type %d",
+ mChannel->getName().string(), mSharedMessage->type);
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+status_t InputConsumer::sendFinishedSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ sendFinishedSignal",
+ mChannel->getName().string());
+#endif
+
+ return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);
+}
+
+status_t InputConsumer::receiveDispatchSignal() {
+#if DEBUG_TRANSPORT_ACTIONS
+ LOGD("channel '%s' consumer ~ receiveDispatchSignal",
+ mChannel->getName().string());
+#endif
+
+ char signal;
+ status_t result = mChannel->receiveSignal(& signal);
+ if (result) {
+ return result;
+ }
+ if (signal != INPUT_SIGNAL_DISPATCH) {
+ LOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
+ mChannel->getName().string(), signal);
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const {
+ keyEvent->initialize(
+ mSharedMessage->deviceId,
+ mSharedMessage->source,
+ mSharedMessage->key.action,
+ mSharedMessage->key.flags,
+ mSharedMessage->key.keyCode,
+ mSharedMessage->key.scanCode,
+ mSharedMessage->key.metaState,
+ mSharedMessage->key.repeatCount,
+ mSharedMessage->key.downTime,
+ mSharedMessage->key.eventTime);
+}
+
+void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
+ motionEvent->initialize(
+ mSharedMessage->deviceId,
+ mSharedMessage->source,
+ mSharedMessage->motion.action,
+ mSharedMessage->motion.flags,
+ mSharedMessage->motion.edgeFlags,
+ mSharedMessage->motion.metaState,
+ mSharedMessage->motion.xOffset,
+ mSharedMessage->motion.yOffset,
+ mSharedMessage->motion.xPrecision,
+ mSharedMessage->motion.yPrecision,
+ mSharedMessage->motion.downTime,
+ mSharedMessage->motion.sampleData[0].eventTime,
+ mSharedMessage->motion.pointerCount,
+ mSharedMessage->motion.pointerIds,
+ mSharedMessage->motion.sampleData[0].coords);
+
+ size_t sampleCount = mSharedMessage->motion.sampleCount;
+ if (sampleCount > 1) {
+ InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData;
+ size_t sampleDataStride = InputMessage::sampleDataStride(
+ mSharedMessage->motion.pointerCount);
+
+ while (--sampleCount > 0) {
+ sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride);
+ motionEvent->addSample(sampleData->eventTime, sampleData->coords);
+ }
+ }
+}
+
+} // namespace android
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index 9b41804..ee186c8 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -59,19 +59,11 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info)
// YUV format from the HAL are handled here
switch (format) {
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCrCb_422_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_P:
case HAL_PIXEL_FORMAT_YCbCr_422_I:
- case HAL_PIXEL_FORMAT_CbYCrY_422_I:
info->bitsPerPixel = 16;
goto done;
- case HAL_PIXEL_FORMAT_YCbCr_420_SP:
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP_TILED:
- case HAL_PIXEL_FORMAT_YCbCr_420_P:
- case HAL_PIXEL_FORMAT_YCbCr_420_I:
- case HAL_PIXEL_FORMAT_CbYCrY_420_I:
+ case HAL_PIXEL_FORMAT_YV12:
info->bitsPerPixel = 12;
done:
info->format = format;
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp
index 66b9576..5694e00 100644
--- a/libs/ui/Rect.cpp
+++ b/libs/ui/Rect.cpp
@@ -18,11 +18,11 @@
namespace android {
-static inline int min(int a, int b) {
+static inline int32_t min(int32_t a, int32_t b) {
return (a<b) ? a : b;
}
-static inline int max(int a, int b) {
+static inline int32_t max(int32_t a, int32_t b) {
return (a>b) ? a : b;
}
@@ -53,7 +53,7 @@ bool Rect::operator < (const Rect& rhs) const
return false;
}
-Rect& Rect::offsetTo(int x, int y)
+Rect& Rect::offsetTo(int32_t x, int32_t y)
{
right -= left - x;
bottom -= top - y;
@@ -62,7 +62,7 @@ Rect& Rect::offsetTo(int x, int y)
return *this;
}
-Rect& Rect::offsetBy(int x, int y)
+Rect& Rect::offsetBy(int32_t x, int32_t y)
{
left += x;
top += y;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 12db908..1994f6a 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -289,7 +289,7 @@ private:
void flushSpan() {
bool merge = false;
if (tail-head == ssize_t(span.size())) {
- Rect const* p = cur;
+ Rect const* p = span.editArray();
Rect const* q = head;
if (p->top == q->bottom) {
merge = true;
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
index 6cc4a5a..aa017b9 100644
--- a/libs/ui/tests/Android.mk
+++ b/libs/ui/tests/Android.mk
@@ -1,16 +1,51 @@
+# Build the unit tests.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- region.cpp
+ifneq ($(TARGET_SIMULATOR),true)
-LOCAL_SHARED_LIBRARIES := \
+# Build the unit tests.
+test_src_files := \
+ InputChannel_test.cpp \
+ InputReader_test.cpp \
+ InputDispatcher_test.cpp \
+ InputPublisherAndConsumer_test.cpp
+
+shared_libraries := \
libcutils \
libutils \
- libui
+ libEGL \
+ libbinder \
+ libpixelflinger \
+ libhardware \
+ libhardware_legacy \
+ libui \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+c_includes := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport
+
+module_tags := eng tests
-LOCAL_MODULE:= test-region
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+ $(eval include $(BUILD_EXECUTABLE)) \
+)
-LOCAL_MODULE_TAGS := tests
+# Build the manual test programs.
+include $(call all-subdir-makefiles)
-include $(BUILD_EXECUTABLE)
+endif \ No newline at end of file
diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp
new file mode 100644
index 0000000..6cec1c0
--- /dev/null
+++ b/libs/ui/tests/InputChannel_test.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputChannelTest : public testing::Test {
+protected:
+ virtual void SetUp() { }
+ virtual void TearDown() { }
+};
+
+
+TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) {
+ // Our purpose here is to verify that the input channel destructor closes the
+ // file descriptors provided to it. One easy way is to provide it with one end
+ // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
+ Pipe fakeAshmem, sendPipe, receivePipe;
+
+ sp<InputChannel> inputChannel = new InputChannel(String8("channel name"),
+ fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd);
+
+ EXPECT_STREQ("channel name", inputChannel->getName().string())
+ << "channel should have provided name";
+ EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd())
+ << "channel should have provided ashmem fd";
+ EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd())
+ << "channel should have provided receive pipe fd";
+ EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd())
+ << "channel should have provided send pipe fd";
+
+ inputChannel.clear(); // destroys input channel
+
+ EXPECT_EQ(-EPIPE, fakeAshmem.readSignal())
+ << "channel should have closed ashmem fd when destroyed";
+ EXPECT_EQ(-EPIPE, receivePipe.writeSignal())
+ << "channel should have closed receive pipe fd when destroyed";
+ EXPECT_EQ(-EPIPE, sendPipe.readSignal())
+ << "channel should have closed send pipe fd when destroyed";
+
+ // clean up fds of Pipe endpoints that were closed so we don't try to close them again
+ fakeAshmem.sendFd = -1;
+ receivePipe.receiveFd = -1;
+ sendPipe.sendFd = -1;
+}
+
+TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ // Name
+ EXPECT_STREQ("channel name (server)", serverChannel->getName().string())
+ << "server channel should have suffixed name";
+ EXPECT_STREQ("channel name (client)", clientChannel->getName().string())
+ << "client channel should have suffixed name";
+
+ // Ashmem uniqueness
+ EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd())
+ << "server and client channel should have different ashmem fds because it was dup'd";
+
+ // Ashmem usability
+ ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd());
+ ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd());
+ uint32_t* serverAshmem = static_cast<uint32_t*>(mmap(NULL, serverAshmemSize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0));
+ uint32_t* clientAshmem = static_cast<uint32_t*>(mmap(NULL, clientAshmemSize,
+ PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0));
+ ASSERT_TRUE(serverAshmem != NULL)
+ << "server channel ashmem should be mappable";
+ ASSERT_TRUE(clientAshmem != NULL)
+ << "client channel ashmem should be mappable";
+ *serverAshmem = 0xf00dd00d;
+ EXPECT_EQ(0xf00dd00d, *clientAshmem)
+ << "ashmem buffer should be shared by client and server";
+ munmap(serverAshmem, serverAshmemSize);
+ munmap(clientAshmem, clientAshmemSize);
+
+ // Server->Client communication
+ EXPECT_EQ(OK, serverChannel->sendSignal('S'))
+ << "server channel should be able to send signal to client channel";
+ char signal;
+ EXPECT_EQ(OK, clientChannel->receiveSignal(& signal))
+ << "client channel should be able to receive signal from server channel";
+ EXPECT_EQ('S', signal)
+ << "client channel should receive the correct signal from server channel";
+
+ // Client->Server communication
+ EXPECT_EQ(OK, clientChannel->sendSignal('c'))
+ << "client channel should be able to send signal to server channel";
+ EXPECT_EQ(OK, serverChannel->receiveSignal(& signal))
+ << "server channel should be able to receive signal from client channel";
+ EXPECT_EQ('c', signal)
+ << "server channel should receive the correct signal from client channel";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ char signal;
+ EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal))
+ << "receiveSignal should have returned WOULD_BLOCK";
+}
+
+TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ serverChannel.clear(); // close server channel
+
+ char signal;
+ EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal))
+ << "receiveSignal should have returned DEAD_OBJECT";
+}
+
+TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) {
+ sp<InputChannel> serverChannel, clientChannel;
+
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ ASSERT_EQ(OK, result)
+ << "should have successfully opened a channel pair";
+
+ serverChannel.clear(); // close server channel
+
+ EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S'))
+ << "sendSignal should have returned DEAD_OBJECT";
+}
+
+
+} // namespace android
diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp
new file mode 100644
index 0000000..8874dfe
--- /dev/null
+++ b/libs/ui/tests/InputDispatcher_test.cpp
@@ -0,0 +1,226 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputDispatcher.h>
+#include <gtest/gtest.h>
+#include <linux/input.h>
+
+namespace android {
+
+// An arbitrary time value.
+static const nsecs_t ARBITRARY_TIME = 1234;
+
+// An arbitrary device id.
+static const int32_t DEVICE_ID = 1;
+
+// An arbitrary injector pid / uid pair that has permission to inject events.
+static const int32_t INJECTOR_PID = 999;
+static const int32_t INJECTOR_UID = 1001;
+
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+protected:
+ virtual ~FakeInputDispatcherPolicy() {
+ }
+
+public:
+ FakeInputDispatcherPolicy() {
+ }
+
+private:
+ virtual void notifyConfigurationChanged(nsecs_t when) {
+ }
+
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputChannel>& inputChannel) {
+ return 0;
+ }
+
+ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
+ }
+
+ virtual nsecs_t getKeyRepeatTimeout() {
+ return 500 * 1000000LL;
+ }
+
+ virtual nsecs_t getKeyRepeatDelay() {
+ return 50 * 1000000LL;
+ }
+
+ virtual int32_t getMaxEventsPerSecond() {
+ return 60;
+ }
+
+ virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
+ int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
+ uint32_t& policyFlags) {
+ }
+
+ virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
+ }
+
+ virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
+ const KeyEvent* keyEvent, uint32_t policyFlags) {
+ return false;
+ }
+
+ virtual void notifySwitch(nsecs_t when,
+ int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {
+ }
+
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+ }
+
+ virtual bool checkInjectEventsPermissionNonReentrant(
+ int32_t injectorPid, int32_t injectorUid) {
+ return false;
+ }
+};
+
+
+// --- InputDispatcherTest ---
+
+class InputDispatcherTest : public testing::Test {
+protected:
+ sp<FakeInputDispatcherPolicy> mFakePolicy;
+ sp<InputDispatcher> mDispatcher;
+
+ virtual void SetUp() {
+ mFakePolicy = new FakeInputDispatcherPolicy();
+ mDispatcher = new InputDispatcher(mFakePolicy);
+ }
+
+ virtual void TearDown() {
+ mFakePolicy.clear();
+ mDispatcher.clear();
+ }
+};
+
+
+TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
+ KeyEvent event;
+
+ // Rejects undefined key actions.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ /*action*/ -1, 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject key events with undefined action.";
+
+ // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ AKEY_EVENT_ACTION_MULTIPLE, 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject key events with ACTION_MULTIPLE.";
+}
+
+TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
+ MotionEvent event;
+ int32_t pointerIds[MAX_POINTERS + 1];
+ PointerCoords pointerCoords[MAX_POINTERS + 1];
+ for (int i = 0; i <= MAX_POINTERS; i++) {
+ pointerIds[i] = i;
+ }
+
+ // Rejects undefined motion actions.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ /*action*/ -1, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with undefined action.";
+
+ // Rejects pointer down with invalid index.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer down index too large.";
+
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer down index too small.";
+
+ // Rejects pointer up with invalid index.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer up index too large.";
+
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer up index too small.";
+
+ // Rejects motion events with invalid number of pointers.
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 0, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with 0 pointers.";
+
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with more than MAX_POINTERS pointers.";
+
+ // Rejects motion events with invalid pointer ids.
+ pointerIds[0] = -1;
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer ids less than 0.";
+
+ pointerIds[0] = MAX_POINTER_ID + 1;
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 1, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
+
+ // Rejects motion events with duplicate pointer ids.
+ pointerIds[0] = 1;
+ pointerIds[1] = 1;
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
+ ARBITRARY_TIME, ARBITRARY_TIME,
+ /*pointerCount*/ 2, pointerIds, pointerCoords);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
+ INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
+ << "Should reject motion events with duplicate pointer ids.";
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
new file mode 100644
index 0000000..952b974
--- /dev/null
+++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp
@@ -0,0 +1,471 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputTransport.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+
+#include "../../utils/tests/TestHelpers.h"
+
+namespace android {
+
+class InputPublisherAndConsumerTest : public testing::Test {
+protected:
+ sp<InputChannel> serverChannel, clientChannel;
+ InputPublisher* mPublisher;
+ InputConsumer* mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+
+ virtual void SetUp() {
+ status_t result = InputChannel::openInputChannelPair(String8("channel name"),
+ serverChannel, clientChannel);
+
+ mPublisher = new InputPublisher(serverChannel);
+ mConsumer = new InputConsumer(clientChannel);
+ }
+
+ virtual void TearDown() {
+ if (mPublisher) {
+ delete mPublisher;
+ mPublisher = NULL;
+ }
+
+ if (mConsumer) {
+ delete mConsumer;
+ mConsumer = NULL;
+ }
+
+ serverChannel.clear();
+ clientChannel.clear();
+ }
+
+ void Initialize();
+ void PublishAndConsumeKeyEvent();
+ void PublishAndConsumeMotionEvent(
+ size_t samplesToAppendBeforeDispatch = 0,
+ size_t samplesToAppendAfterDispatch = 0);
+};
+
+TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
+ EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get());
+ EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get());
+}
+
+void InputPublisherAndConsumerTest::Initialize() {
+ status_t status;
+
+ status = mPublisher->initialize();
+ ASSERT_EQ(OK, status)
+ << "publisher initialize should return OK";
+
+ status = mConsumer->initialize();
+ ASSERT_EQ(OK, status)
+ << "consumer initialize should return OK";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
+ status_t status;
+
+ const int32_t deviceId = 1;
+ const int32_t source = AINPUT_SOURCE_KEYBOARD;
+ const int32_t action = AKEY_EVENT_ACTION_DOWN;
+ const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ const int32_t keyCode = AKEYCODE_ENTER;
+ const int32_t scanCode = 13;
+ const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ const int32_t repeatCount = 1;
+ const nsecs_t downTime = 3;
+ const nsecs_t eventTime = 4;
+
+ status = mPublisher->publishKeyEvent(deviceId, source, action, flags,
+ keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+ ASSERT_EQ(OK, status)
+ << "publisher publishKeyEvent should return OK";
+
+ status = mPublisher->sendDispatchSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher sendDispatchSignal should return OK";
+
+ status = mConsumer->receiveDispatchSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer receiveDispatchSignal should return OK";
+
+ InputEvent* event;
+ status = mConsumer->consume(& mEventFactory, & event);
+ ASSERT_EQ(OK, status)
+ << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != NULL)
+ << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType())
+ << "consumer should have returned a key event";
+
+ KeyEvent* keyEvent = static_cast<KeyEvent*>(event);
+ EXPECT_EQ(deviceId, keyEvent->getDeviceId());
+ EXPECT_EQ(source, keyEvent->getSource());
+ EXPECT_EQ(action, keyEvent->getAction());
+ EXPECT_EQ(flags, keyEvent->getFlags());
+ EXPECT_EQ(keyCode, keyEvent->getKeyCode());
+ EXPECT_EQ(scanCode, keyEvent->getScanCode());
+ EXPECT_EQ(metaState, keyEvent->getMetaState());
+ EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
+ EXPECT_EQ(downTime, keyEvent->getDownTime());
+ EXPECT_EQ(eventTime, keyEvent->getEventTime());
+
+ status = mConsumer->sendFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer sendFinishedSignal should return OK";
+
+ status = mPublisher->receiveFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher receiveFinishedSignal should return OK";
+
+ status = mPublisher->reset();
+ ASSERT_EQ(OK, status)
+ << "publisher reset should return OK";
+}
+
+void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent(
+ size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) {
+ status_t status;
+
+ const int32_t deviceId = 1;
+ const int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ const int32_t action = AMOTION_EVENT_ACTION_MOVE;
+ const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
+ const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ const float xOffset = -10;
+ const float yOffset = -20;
+ const float xPrecision = 0.25;
+ const float yPrecision = 0.5;
+ const nsecs_t downTime = 3;
+ const size_t pointerCount = 3;
+ const int32_t pointerIds[pointerCount] = { 2, 0, 1 };
+
+ Vector<nsecs_t> sampleEventTimes;
+ Vector<PointerCoords> samplePointerCoords;
+
+ for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) {
+ sampleEventTimes.push(i + 10);
+ for (size_t j = 0; j < pointerCount; j++) {
+ samplePointerCoords.push();
+ samplePointerCoords.editTop().x = 100 * i + j;
+ samplePointerCoords.editTop().y = 200 * i + j;
+ samplePointerCoords.editTop().pressure = 0.5 * i + j;
+ samplePointerCoords.editTop().size = 0.7 * i + j;
+ samplePointerCoords.editTop().touchMajor = 1.5 * i + j;
+ samplePointerCoords.editTop().touchMinor = 1.7 * i + j;
+ samplePointerCoords.editTop().toolMajor = 2.5 * i + j;
+ samplePointerCoords.editTop().toolMinor = 2.7 * i + j;
+ samplePointerCoords.editTop().orientation = 3.5 * i + j;
+ }
+ }
+
+ status = mPublisher->publishMotionEvent(deviceId, source, action, flags, edgeFlags,
+ metaState, xOffset, yOffset, xPrecision, yPrecision,
+ downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array());
+ ASSERT_EQ(OK, status)
+ << "publisher publishMotionEvent should return OK";
+
+ for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) {
+ size_t sampleIndex = i + 1;
+ status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
+ samplePointerCoords.array() + sampleIndex * pointerCount);
+ ASSERT_EQ(OK, status)
+ << "publisher appendMotionEvent should return OK";
+ }
+
+ status = mPublisher->sendDispatchSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher sendDispatchSignal should return OK";
+
+ for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) {
+ size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch;
+ status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex],
+ samplePointerCoords.array() + sampleIndex * pointerCount);
+ ASSERT_EQ(OK, status)
+ << "publisher appendMotionEvent should return OK";
+ }
+
+ status = mConsumer->receiveDispatchSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer receiveDispatchSignal should return OK";
+
+ InputEvent* event;
+ status = mConsumer->consume(& mEventFactory, & event);
+ ASSERT_EQ(OK, status)
+ << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != NULL)
+ << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+ << "consumer should have returned a motion event";
+
+ size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch;
+
+ MotionEvent* motionEvent = static_cast<MotionEvent*>(event);
+ EXPECT_EQ(deviceId, motionEvent->getDeviceId());
+ EXPECT_EQ(source, motionEvent->getSource());
+ EXPECT_EQ(action, motionEvent->getAction());
+ EXPECT_EQ(flags, motionEvent->getFlags());
+ EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags());
+ EXPECT_EQ(metaState, motionEvent->getMetaState());
+ EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
+ EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+ EXPECT_EQ(downTime, motionEvent->getDownTime());
+ EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+ EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
+ EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize());
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(i));
+ }
+
+ for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) {
+ SCOPED_TRACE(sampleIndex);
+ EXPECT_EQ(sampleEventTimes[sampleIndex],
+ motionEvent->getHistoricalEventTime(sampleIndex));
+ for (size_t i = 0; i < pointerCount; i++) {
+ SCOPED_TRACE(i);
+ size_t offset = sampleIndex * pointerCount + i;
+ EXPECT_EQ(samplePointerCoords[offset].x,
+ motionEvent->getHistoricalRawX(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].y,
+ motionEvent->getHistoricalRawY(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].x + xOffset,
+ motionEvent->getHistoricalX(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].y + yOffset,
+ motionEvent->getHistoricalY(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].pressure,
+ motionEvent->getHistoricalPressure(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].size,
+ motionEvent->getHistoricalSize(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].touchMajor,
+ motionEvent->getHistoricalTouchMajor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].touchMinor,
+ motionEvent->getHistoricalTouchMinor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].toolMajor,
+ motionEvent->getHistoricalToolMajor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].toolMinor,
+ motionEvent->getHistoricalToolMinor(i, sampleIndex));
+ EXPECT_EQ(samplePointerCoords[offset].orientation,
+ motionEvent->getHistoricalOrientation(i, sampleIndex));
+ }
+ }
+
+ SCOPED_TRACE(lastSampleIndex);
+ EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime());
+ for (size_t i = 0; i < pointerCount; i++) {
+ SCOPED_TRACE(i);
+ size_t offset = lastSampleIndex * pointerCount + i;
+ EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i));
+ EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i));
+ EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i));
+ EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i));
+ EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i));
+ EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i));
+ EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i));
+ EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i));
+ EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i));
+ EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i));
+ }
+
+ status = mConsumer->sendFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "consumer sendFinishedSignal should return OK";
+
+ status = mPublisher->receiveFinishedSignal();
+ ASSERT_EQ(OK, status)
+ << "publisher receiveFinishedSignal should return OK";
+
+ status = mPublisher->reset();
+ ASSERT_EQ(OK, status)
+ << "publisher reset should return OK";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ ASSERT_EQ(OK, status)
+ << "publisher publishKeyEvent should return OK first time";
+
+ status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ ASSERT_EQ(INVALID_OPERATION, status)
+ << "publisher publishKeyEvent should return INVALID_OPERATION because "
+ "the publisher was not reset";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = 1;
+ int32_t pointerIds[pointerCount] = { 0 };
+ PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(OK, status)
+ << "publisher publishMotionEvent should return OK";
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(INVALID_OPERATION, status)
+ << "publisher publishMotionEvent should return INVALID_OPERATION because ";
+ "the publisher was not reset";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = 0;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(BAD_VALUE, status)
+ << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = MAX_POINTERS + 1;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(BAD_VALUE, status)
+ << "publisher publishMotionEvent should return BAD_VALUE";
+}
+
+TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0));
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4));
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ PointerCoords pointerCoords[1];
+ status = mPublisher->appendMotionSample(0, pointerCoords);
+ ASSERT_EQ(INVALID_OPERATION, status)
+ << "publisher appendMotionSample should return INVALID_OPERATION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = MAX_POINTERS;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_DOWN,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(OK, status);
+
+ status = mPublisher->appendMotionSample(0, pointerCoords);
+ ASSERT_EQ(INVALID_OPERATION, status)
+ << "publisher appendMotionSample should return INVALID_OPERATION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = MAX_POINTERS;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(OK, status);
+
+ status = mPublisher->sendDispatchSignal();
+ ASSERT_EQ(OK, status);
+
+ status = mConsumer->receiveDispatchSignal();
+ ASSERT_EQ(OK, status);
+
+ InputEvent* event;
+ status = mConsumer->consume(& mEventFactory, & event);
+ ASSERT_EQ(OK, status);
+
+ status = mPublisher->appendMotionSample(0, pointerCoords);
+ ASSERT_EQ(status_t(FAILED_TRANSACTION), status)
+ << "publisher appendMotionSample should return FAILED_TRANSACTION";
+}
+
+TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) {
+ status_t status;
+ ASSERT_NO_FATAL_FAILURE(Initialize());
+
+ const size_t pointerCount = MAX_POINTERS;
+ int32_t pointerIds[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ status = mPublisher->publishMotionEvent(0, 0, AMOTION_EVENT_ACTION_MOVE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords);
+ ASSERT_EQ(OK, status);
+
+ for (int count = 1;; count++) {
+ ASSERT_LT(count, 100000) << "should eventually reach OOM";
+
+ status = mPublisher->appendMotionSample(0, pointerCoords);
+ if (status != OK) {
+ ASSERT_GT(count, 12) << "should be able to add at least a dozen samples";
+ ASSERT_EQ(NO_MEMORY, status)
+ << "publisher appendMotionSample should return NO_MEMORY when buffer is full";
+ break;
+ }
+ }
+
+ status = mPublisher->appendMotionSample(0, pointerCoords);
+ ASSERT_EQ(NO_MEMORY, status)
+ << "publisher appendMotionSample should return NO_MEMORY persistently until reset";
+}
+
+} // namespace android
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
new file mode 100644
index 0000000..de4b05a
--- /dev/null
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -0,0 +1,3368 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <ui/InputReader.h>
+#include <utils/List.h>
+#include <gtest/gtest.h>
+#include <math.h>
+
+namespace android {
+
+// An arbitrary time value.
+static const nsecs_t ARBITRARY_TIME = 1234;
+
+// Arbitrary display properties.
+static const int32_t DISPLAY_ID = 0;
+static const int32_t DISPLAY_WIDTH = 480;
+static const int32_t DISPLAY_HEIGHT = 800;
+
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
+template<typename T>
+static inline T min(T a, T b) {
+ return a < b ? a : b;
+}
+
+static inline float avg(float x, float y) {
+ return (x + y) / 2;
+}
+
+
+// --- FakeInputReaderPolicy ---
+
+class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+ struct DisplayInfo {
+ int32_t width;
+ int32_t height;
+ int32_t orientation;
+ };
+
+ KeyedVector<int32_t, DisplayInfo> mDisplayInfos;
+ bool mFilterTouchEvents;
+ bool mFilterJumpyTouchEvents;
+ KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions;
+ KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations;
+ Vector<String8> mExcludedDeviceNames;
+
+protected:
+ virtual ~FakeInputReaderPolicy() { }
+
+public:
+ FakeInputReaderPolicy() :
+ mFilterTouchEvents(false), mFilterJumpyTouchEvents(false) {
+ }
+
+ void removeDisplayInfo(int32_t displayId) {
+ mDisplayInfos.removeItem(displayId);
+ }
+
+ void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) {
+ removeDisplayInfo(displayId);
+
+ DisplayInfo info;
+ info.width = width;
+ info.height = height;
+ info.orientation = orientation;
+ mDisplayInfos.add(displayId, info);
+ }
+
+ void setFilterTouchEvents(bool enabled) {
+ mFilterTouchEvents = enabled;
+ }
+
+ void setFilterJumpyTouchEvents(bool enabled) {
+ mFilterJumpyTouchEvents = enabled;
+ }
+
+ void addInputDeviceCalibration(const String8& deviceName,
+ const InputDeviceCalibration& calibration) {
+ mInputDeviceCalibrations.add(deviceName, calibration);
+ }
+
+ void addInputDeviceCalibrationProperty(const String8& deviceName,
+ const String8& key, const String8& value) {
+ ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
+ if (index < 0) {
+ index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration());
+ }
+ mInputDeviceCalibrations.editValueAt(index).addProperty(key, value);
+ }
+
+ void addVirtualKeyDefinition(const String8& deviceName,
+ const VirtualKeyDefinition& definition) {
+ if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) {
+ mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>());
+ }
+
+ mVirtualKeyDefinitions.editValueFor(deviceName).push(definition);
+ }
+
+ void addExcludedDeviceName(const String8& deviceName) {
+ mExcludedDeviceNames.push(deviceName);
+ }
+
+private:
+ virtual bool getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation) {
+ ssize_t index = mDisplayInfos.indexOfKey(displayId);
+ if (index >= 0) {
+ const DisplayInfo& info = mDisplayInfos.valueAt(index);
+ if (width) {
+ *width = info.width;
+ }
+ if (height) {
+ *height = info.height;
+ }
+ if (orientation) {
+ *orientation = info.orientation;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool filterTouchEvents() {
+ return mFilterTouchEvents;
+ }
+
+ virtual bool filterJumpyTouchEvents() {
+ return mFilterJumpyTouchEvents;
+ }
+
+ virtual void getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
+ ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName);
+ if (index >= 0) {
+ outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index));
+ }
+ }
+
+ virtual void getInputDeviceCalibration(const String8& deviceName,
+ InputDeviceCalibration& outCalibration) {
+ ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName);
+ if (index >= 0) {
+ outCalibration = mInputDeviceCalibrations.valueAt(index);
+ }
+ }
+
+ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
+ outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
+ }
+};
+
+
+// --- FakeInputDispatcher ---
+
+class FakeInputDispatcher : public InputDispatcherInterface {
+public:
+ struct NotifyConfigurationChangedArgs {
+ nsecs_t eventTime;
+ };
+
+ struct NotifyKeyArgs {
+ nsecs_t eventTime;
+ int32_t deviceId;
+ int32_t source;
+ uint32_t policyFlags;
+ int32_t action;
+ int32_t flags;
+ int32_t keyCode;
+ int32_t scanCode;
+ int32_t metaState;
+ nsecs_t downTime;
+ };
+
+ struct NotifyMotionArgs {
+ nsecs_t eventTime;
+ int32_t deviceId;
+ int32_t source;
+ uint32_t policyFlags;
+ int32_t action;
+ int32_t flags;
+ int32_t metaState;
+ int32_t edgeFlags;
+ uint32_t pointerCount;
+ Vector<int32_t> pointerIds;
+ Vector<PointerCoords> pointerCoords;
+ float xPrecision;
+ float yPrecision;
+ nsecs_t downTime;
+ };
+
+ struct NotifySwitchArgs {
+ nsecs_t when;
+ int32_t switchCode;
+ int32_t switchValue;
+ uint32_t policyFlags;
+ };
+
+private:
+ List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgs;
+ List<NotifyKeyArgs> mNotifyKeyArgs;
+ List<NotifyMotionArgs> mNotifyMotionArgs;
+ List<NotifySwitchArgs> mNotifySwitchArgs;
+
+protected:
+ virtual ~FakeInputDispatcher() { }
+
+public:
+ FakeInputDispatcher() {
+ }
+
+ void assertNotifyConfigurationChangedWasCalled(NotifyConfigurationChangedArgs* outArgs = NULL) {
+ ASSERT_FALSE(mNotifyConfigurationChangedArgs.empty())
+ << "Expected notifyConfigurationChanged() to have been called.";
+ if (outArgs) {
+ *outArgs = *mNotifyConfigurationChangedArgs.begin();
+ }
+ mNotifyConfigurationChangedArgs.erase(mNotifyConfigurationChangedArgs.begin());
+ }
+
+ void assertNotifyKeyWasCalled(NotifyKeyArgs* outArgs = NULL) {
+ ASSERT_FALSE(mNotifyKeyArgs.empty())
+ << "Expected notifyKey() to have been called.";
+ if (outArgs) {
+ *outArgs = *mNotifyKeyArgs.begin();
+ }
+ mNotifyKeyArgs.erase(mNotifyKeyArgs.begin());
+ }
+
+ void assertNotifyKeyWasNotCalled() {
+ ASSERT_TRUE(mNotifyKeyArgs.empty())
+ << "Expected notifyKey() to not have been called.";
+ }
+
+ void assertNotifyMotionWasCalled(NotifyMotionArgs* outArgs = NULL) {
+ ASSERT_FALSE(mNotifyMotionArgs.empty())
+ << "Expected notifyMotion() to have been called.";
+ if (outArgs) {
+ *outArgs = *mNotifyMotionArgs.begin();
+ }
+ mNotifyMotionArgs.erase(mNotifyMotionArgs.begin());
+ }
+
+ void assertNotifyMotionWasNotCalled() {
+ ASSERT_TRUE(mNotifyMotionArgs.empty())
+ << "Expected notifyMotion() to not have been called.";
+ }
+
+ void assertNotifySwitchWasCalled(NotifySwitchArgs* outArgs = NULL) {
+ ASSERT_FALSE(mNotifySwitchArgs.empty())
+ << "Expected notifySwitch() to have been called.";
+ if (outArgs) {
+ *outArgs = *mNotifySwitchArgs.begin();
+ }
+ mNotifySwitchArgs.erase(mNotifySwitchArgs.begin());
+ }
+
+private:
+ virtual void notifyConfigurationChanged(nsecs_t eventTime) {
+ NotifyConfigurationChangedArgs args;
+ args.eventTime = eventTime;
+ mNotifyConfigurationChangedArgs.push_back(args);
+ }
+
+ virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+ NotifyKeyArgs args;
+ args.eventTime = eventTime;
+ args.deviceId = deviceId;
+ args.source = source;
+ args.policyFlags = policyFlags;
+ args.action = action;
+ args.flags = flags;
+ args.keyCode = keyCode;
+ args.scanCode = scanCode;
+ args.metaState = metaState;
+ args.downTime = downTime;
+ mNotifyKeyArgs.push_back(args);
+ }
+
+ virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source,
+ uint32_t policyFlags, int32_t action, int32_t flags,
+ int32_t metaState, int32_t edgeFlags,
+ uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
+ float xPrecision, float yPrecision, nsecs_t downTime) {
+ NotifyMotionArgs args;
+ args.eventTime = eventTime;
+ args.deviceId = deviceId;
+ args.source = source;
+ args.policyFlags = policyFlags;
+ args.action = action;
+ args.flags = flags;
+ args.metaState = metaState;
+ args.edgeFlags = edgeFlags;
+ args.pointerCount = pointerCount;
+ args.pointerIds.clear();
+ args.pointerIds.appendArray(pointerIds, pointerCount);
+ args.pointerCoords.clear();
+ args.pointerCoords.appendArray(pointerCoords, pointerCount);
+ args.xPrecision = xPrecision;
+ args.yPrecision = yPrecision;
+ args.downTime = downTime;
+ mNotifyMotionArgs.push_back(args);
+ }
+
+ virtual void notifySwitch(nsecs_t when,
+ int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {
+ NotifySwitchArgs args;
+ args.when = when;
+ args.switchCode = switchCode;
+ args.switchValue = switchValue;
+ args.policyFlags = policyFlags;
+ mNotifySwitchArgs.push_back(args);
+ }
+
+ virtual void dump(String8& dump) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
+ virtual void dispatchOnce() {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
+ virtual int32_t injectInputEvent(const InputEvent* event,
+ int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ virtual void setInputWindows(const Vector<InputWindow>& inputWindows) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
+ virtual void setFocusedApplication(const InputApplication* inputApplication) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
+ virtual void setInputDispatchMode(bool enabled, bool frozen) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ }
+
+ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ return 0;
+ }
+
+ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) {
+ ADD_FAILURE() << "Should never be called by input reader.";
+ return 0;
+ }
+};
+
+
+// --- FakeEventHub ---
+
+class FakeEventHub : public EventHubInterface {
+ struct KeyInfo {
+ int32_t keyCode;
+ uint32_t flags;
+ };
+
+ struct Device {
+ String8 name;
+ uint32_t classes;
+ KeyedVector<int, RawAbsoluteAxisInfo> axes;
+ KeyedVector<int32_t, int32_t> keyCodeStates;
+ KeyedVector<int32_t, int32_t> scanCodeStates;
+ KeyedVector<int32_t, int32_t> switchStates;
+ KeyedVector<int32_t, KeyInfo> keys;
+
+ Device(const String8& name, uint32_t classes) :
+ name(name), classes(classes) {
+ }
+ };
+
+ KeyedVector<int32_t, Device*> mDevices;
+ Vector<String8> mExcludedDevices;
+ List<RawEvent> mEvents;
+
+protected:
+ virtual ~FakeEventHub() {
+ for (size_t i = 0; i < mDevices.size(); i++) {
+ delete mDevices.valueAt(i);
+ }
+ }
+
+public:
+ FakeEventHub() { }
+
+ void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ Device* device = new Device(name, classes);
+ mDevices.add(deviceId, device);
+
+ enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0, 0, 0);
+ }
+
+ void removeDevice(int32_t deviceId) {
+ delete mDevices.valueFor(deviceId);
+ mDevices.removeItem(deviceId);
+
+ enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0, 0, 0);
+ }
+
+ void finishDeviceScan() {
+ enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0);
+ }
+
+ void addAxis(int32_t deviceId, int axis,
+ int32_t minValue, int32_t maxValue, int flat, int fuzz) {
+ Device* device = getDevice(deviceId);
+
+ RawAbsoluteAxisInfo info;
+ info.valid = true;
+ info.minValue = minValue;
+ info.maxValue = maxValue;
+ info.flat = flat;
+ info.fuzz = fuzz;
+ device->axes.add(axis, info);
+ }
+
+ void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->keyCodeStates.replaceValueFor(keyCode, state);
+ }
+
+ void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->scanCodeStates.replaceValueFor(scanCode, state);
+ }
+
+ void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) {
+ Device* device = getDevice(deviceId);
+ device->switchStates.replaceValueFor(switchCode, state);
+ }
+
+ void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) {
+ Device* device = getDevice(deviceId);
+ KeyInfo info;
+ info.keyCode = keyCode;
+ info.flags = flags;
+ device->keys.add(scanCode, info);
+ }
+
+ Vector<String8>& getExcludedDevices() {
+ return mExcludedDevices;
+ }
+
+ void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
+ int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) {
+ RawEvent event;
+ event.when = when;
+ event.deviceId = deviceId;
+ event.type = type;
+ event.scanCode = scanCode;
+ event.keyCode = keyCode;
+ event.value = value;
+ event.flags = flags;
+ mEvents.push_back(event);
+ }
+
+ void assertQueueIsEmpty() {
+ ASSERT_EQ(size_t(0), mEvents.size())
+ << "Expected the event queue to be empty (fully consumed).";
+ }
+
+private:
+ Device* getDevice(int32_t deviceId) const {
+ ssize_t index = mDevices.indexOfKey(deviceId);
+ return index >= 0 ? mDevices.valueAt(index) : NULL;
+ }
+
+ virtual uint32_t getDeviceClasses(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->classes : 0;
+ }
+
+ virtual String8 getDeviceName(int32_t deviceId) const {
+ Device* device = getDevice(deviceId);
+ return device ? device->name : String8("unknown");
+ }
+
+ virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+ RawAbsoluteAxisInfo* outAxisInfo) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->axes.indexOfKey(axis);
+ if (index >= 0) {
+ *outAxisInfo = device->axes.valueAt(index);
+ return OK;
+ }
+ }
+ return -1;
+ }
+
+ virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keys.indexOfKey(scancode);
+ if (index >= 0) {
+ if (outKeycode) {
+ *outKeycode = device->keys.valueAt(index).keyCode;
+ }
+ if (outFlags) {
+ *outFlags = device->keys.valueAt(index).flags;
+ }
+ return OK;
+ }
+ }
+ return NAME_NOT_FOUND;
+ }
+
+ virtual void addExcludedDevice(const char* deviceName) {
+ mExcludedDevices.add(String8(deviceName));
+ }
+
+ virtual bool getEvent(RawEvent* outEvent) {
+ if (mEvents.empty()) {
+ return false;
+ }
+
+ *outEvent = *mEvents.begin();
+ mEvents.erase(mEvents.begin());
+ return true;
+ }
+
+ virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
+ if (index >= 0) {
+ return device->scanCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
+ if (index >= 0) {
+ return device->keyCodeStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
+ Device* device = getDevice(deviceId);
+ if (device) {
+ ssize_t index = device->switchStates.indexOfKey(sw);
+ if (index >= 0) {
+ return device->switchStates.valueAt(index);
+ }
+ }
+ return AKEY_STATE_UNKNOWN;
+ }
+
+ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+ uint8_t* outFlags) const {
+ bool result = false;
+ Device* device = getDevice(deviceId);
+ if (device) {
+ for (size_t i = 0; i < numCodes; i++) {
+ for (size_t j = 0; j < device->keys.size(); j++) {
+ if (keyCodes[i] == device->keys.valueAt(j).keyCode) {
+ outFlags[i] = 1;
+ result = true;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ virtual void dump(String8& dump) {
+ }
+};
+
+
+// --- FakeInputReaderContext ---
+
+class FakeInputReaderContext : public InputReaderContext {
+ sp<EventHubInterface> mEventHub;
+ sp<InputReaderPolicyInterface> mPolicy;
+ sp<InputDispatcherInterface> mDispatcher;
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+
+public:
+ FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher),
+ mGlobalMetaState(0) {
+ }
+
+ virtual ~FakeInputReaderContext() { }
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) {
+ mGlobalMetaState = state;
+ }
+
+private:
+ virtual void updateGlobalMetaState() {
+ mUpdateGlobalMetaStateWasCalled = true;
+ }
+
+ virtual int32_t getGlobalMetaState() {
+ return mGlobalMetaState;
+ }
+
+ virtual EventHubInterface* getEventHub() {
+ return mEventHub.get();
+ }
+
+ virtual InputReaderPolicyInterface* getPolicy() {
+ return mPolicy.get();
+ }
+
+ virtual InputDispatcherInterface* getDispatcher() {
+ return mDispatcher.get();
+ }
+};
+
+
+// --- FakeInputMapper ---
+
+class FakeInputMapper : public InputMapper {
+ uint32_t mSources;
+ int32_t mKeyboardType;
+ int32_t mMetaState;
+ KeyedVector<int32_t, int32_t> mKeyCodeStates;
+ KeyedVector<int32_t, int32_t> mScanCodeStates;
+ KeyedVector<int32_t, int32_t> mSwitchStates;
+ Vector<int32_t> mSupportedKeyCodes;
+ RawEvent mLastEvent;
+
+ bool mConfigureWasCalled;
+ bool mResetWasCalled;
+ bool mProcessWasCalled;
+
+public:
+ FakeInputMapper(InputDevice* device, uint32_t sources) :
+ InputMapper(device),
+ mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
+ mMetaState(0),
+ mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) {
+ }
+
+ virtual ~FakeInputMapper() { }
+
+ void setKeyboardType(int32_t keyboardType) {
+ mKeyboardType = keyboardType;
+ }
+
+ void setMetaState(int32_t metaState) {
+ mMetaState = metaState;
+ }
+
+ void assertConfigureWasCalled() {
+ ASSERT_TRUE(mConfigureWasCalled)
+ << "Expected configure() to have been called.";
+ mConfigureWasCalled = false;
+ }
+
+ void assertResetWasCalled() {
+ ASSERT_TRUE(mResetWasCalled)
+ << "Expected reset() to have been called.";
+ mResetWasCalled = false;
+ }
+
+ void assertProcessWasCalled(RawEvent* outLastEvent = NULL) {
+ ASSERT_TRUE(mProcessWasCalled)
+ << "Expected process() to have been called.";
+ if (outLastEvent) {
+ *outLastEvent = mLastEvent;
+ }
+ mProcessWasCalled = false;
+ }
+
+ void setKeyCodeState(int32_t keyCode, int32_t state) {
+ mKeyCodeStates.replaceValueFor(keyCode, state);
+ }
+
+ void setScanCodeState(int32_t scanCode, int32_t state) {
+ mScanCodeStates.replaceValueFor(scanCode, state);
+ }
+
+ void setSwitchState(int32_t switchCode, int32_t state) {
+ mSwitchStates.replaceValueFor(switchCode, state);
+ }
+
+ void addSupportedKeyCode(int32_t keyCode) {
+ mSupportedKeyCodes.add(keyCode);
+ }
+
+private:
+ virtual uint32_t getSources() {
+ return mSources;
+ }
+
+ virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+ InputMapper::populateDeviceInfo(deviceInfo);
+
+ if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
+ deviceInfo->setKeyboardType(mKeyboardType);
+ }
+ }
+
+ virtual void configure() {
+ mConfigureWasCalled = true;
+ }
+
+ virtual void reset() {
+ mResetWasCalled = true;
+ }
+
+ virtual void process(const RawEvent* rawEvent) {
+ mLastEvent = *rawEvent;
+ mProcessWasCalled = true;
+ }
+
+ virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
+ ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
+ return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ ssize_t index = mScanCodeStates.indexOfKey(scanCode);
+ return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) {
+ ssize_t index = mSwitchStates.indexOfKey(switchCode);
+ return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
+ }
+
+ virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
+ const int32_t* keyCodes, uint8_t* outFlags) {
+ bool result = false;
+ for (size_t i = 0; i < numCodes; i++) {
+ for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
+ if (keyCodes[i] == mSupportedKeyCodes[j]) {
+ outFlags[i] = 1;
+ result = true;
+ }
+ }
+ }
+ return result;
+ }
+
+ virtual int32_t getMetaState() {
+ return mMetaState;
+ }
+};
+
+
+// --- InstrumentedInputReader ---
+
+class InstrumentedInputReader : public InputReader {
+ InputDevice* mNextDevice;
+
+public:
+ InstrumentedInputReader(const sp<EventHubInterface>& eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ const sp<InputDispatcherInterface>& dispatcher) :
+ InputReader(eventHub, policy, dispatcher) {
+ }
+
+ virtual ~InstrumentedInputReader() {
+ if (mNextDevice) {
+ delete mNextDevice;
+ }
+ }
+
+ void setNextDevice(InputDevice* device) {
+ mNextDevice = device;
+ }
+
+protected:
+ virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ if (mNextDevice) {
+ InputDevice* device = mNextDevice;
+ mNextDevice = NULL;
+ return device;
+ }
+ return InputReader::createDevice(deviceId, name, classes);
+ }
+
+ friend class InputReaderTest;
+};
+
+
+// --- InputReaderTest ---
+
+class InputReaderTest : public testing::Test {
+protected:
+ sp<FakeInputDispatcher> mFakeDispatcher;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ sp<FakeEventHub> mFakeEventHub;
+ sp<InstrumentedInputReader> mReader;
+
+ virtual void SetUp() {
+ mFakeEventHub = new FakeEventHub();
+ mFakePolicy = new FakeInputReaderPolicy();
+ mFakeDispatcher = new FakeInputDispatcher();
+
+ mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+ }
+
+ virtual void TearDown() {
+ mReader.clear();
+
+ mFakeDispatcher.clear();
+ mFakePolicy.clear();
+ mFakeEventHub.clear();
+ }
+
+ void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+ mFakeEventHub->addDevice(deviceId, name, classes);
+ mFakeEventHub->finishDeviceScan();
+ mReader->loopOnce();
+ mReader->loopOnce();
+ mFakeEventHub->assertQueueIsEmpty();
+ }
+
+ FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId,
+ const String8& name, uint32_t classes, uint32_t sources) {
+ InputDevice* device = new InputDevice(mReader.get(), deviceId, name);
+ FakeInputMapper* mapper = new FakeInputMapper(device, sources);
+ device->addMapper(mapper);
+ mReader->setNextDevice(device);
+ addDevice(deviceId, name, classes);
+ return mapper;
+ }
+};
+
+TEST_F(InputReaderTest, GetInputConfiguration_WhenNoDevices_ReturnsDefaults) {
+ InputConfiguration config;
+ mReader->getInputConfiguration(&config);
+
+ ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard);
+ ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation);
+ ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
+}
+
+TEST_F(InputReaderTest, GetInputConfiguration_WhenAlphabeticKeyboardPresent_ReturnsQwertyKeyboard) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("keyboard"),
+ INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY));
+
+ InputConfiguration config;
+ mReader->getInputConfiguration(&config);
+
+ ASSERT_EQ(InputConfiguration::KEYBOARD_QWERTY, config.keyboard);
+ ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation);
+ ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
+}
+
+TEST_F(InputReaderTest, GetInputConfiguration_WhenTouchScreenPresent_ReturnsFingerTouchScreen) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("touchscreen"),
+ INPUT_DEVICE_CLASS_TOUCHSCREEN));
+
+ InputConfiguration config;
+ mReader->getInputConfiguration(&config);
+
+ ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard);
+ ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation);
+ ASSERT_EQ(InputConfiguration::TOUCHSCREEN_FINGER, config.touchScreen);
+}
+
+TEST_F(InputReaderTest, GetInputConfiguration_WhenTrackballPresent_ReturnsTrackballNavigation) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("trackball"),
+ INPUT_DEVICE_CLASS_TRACKBALL));
+
+ InputConfiguration config;
+ mReader->getInputConfiguration(&config);
+
+ ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard);
+ ASSERT_EQ(InputConfiguration::NAVIGATION_TRACKBALL, config.navigation);
+ ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
+}
+
+TEST_F(InputReaderTest, GetInputConfiguration_WhenDPadPresent_ReturnsDPadNavigation) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("dpad"),
+ INPUT_DEVICE_CLASS_DPAD));
+
+ InputConfiguration config;
+ mReader->getInputConfiguration(&config);
+
+ ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard);
+ ASSERT_EQ(InputConfiguration::NAVIGATION_DPAD, config.navigation);
+ ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
+}
+
+TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
+ INPUT_DEVICE_CLASS_KEYBOARD));
+
+ InputDeviceInfo info;
+ status_t result = mReader->getInputDeviceInfo(1, &info);
+
+ ASSERT_EQ(OK, result);
+ ASSERT_EQ(1, info.getId());
+ ASSERT_STREQ("keyboard", info.getName().string());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, info.getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, info.getSources());
+ ASSERT_EQ(size_t(0), info.getMotionRanges().size());
+}
+
+TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsInvalid) {
+ InputDeviceInfo info;
+ status_t result = mReader->getInputDeviceInfo(-1, &info);
+
+ ASSERT_EQ(NAME_NOT_FOUND, result);
+}
+
+TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) {
+ addDevice(1, String8("ignored"), 0); // no classes so device will be ignored
+
+ InputDeviceInfo info;
+ status_t result = mReader->getInputDeviceInfo(1, &info);
+
+ ASSERT_EQ(NAME_NOT_FOUND, result);
+}
+
+TEST_F(InputReaderTest, GetInputDeviceIds) {
+ ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
+ INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY));
+ ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("trackball"),
+ INPUT_DEVICE_CLASS_TRACKBALL));
+
+ Vector<int32_t> ids;
+ mReader->getInputDeviceIds(ids);
+
+ ASSERT_EQ(size_t(2), ids.size());
+ ASSERT_EQ(1, ids[0]);
+ ASSERT_EQ(2, ids[1]);
+}
+
+TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
+ FakeInputMapper* mapper = NULL;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+ mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
+ AINPUT_SOURCE_ANY, AKEYCODE_A))
+ << "Should return unknown when the device id is >= 0 but unknown.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1,
+ AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1,
+ AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(-1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
+}
+
+TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
+ FakeInputMapper* mapper = NULL;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+ mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
+ AINPUT_SOURCE_ANY, KEY_A))
+ << "Should return unknown when the device id is >= 0 but unknown.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1,
+ AINPUT_SOURCE_TRACKBALL, KEY_A))
+ << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
+ << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1,
+ AINPUT_SOURCE_TRACKBALL, KEY_A))
+ << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(-1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
+ << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
+}
+
+TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
+ FakeInputMapper* mapper = NULL;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+ mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
+ AINPUT_SOURCE_ANY, SW_LID))
+ << "Should return unknown when the device id is >= 0 but unknown.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1,
+ AINPUT_SOURCE_TRACKBALL, SW_LID))
+ << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
+ << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1,
+ AINPUT_SOURCE_TRACKBALL, SW_LID))
+ << "Should return unknown when the device id is < 0 but the sources are not supported by any device.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(-1,
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
+ << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
+}
+
+TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
+ FakeInputMapper* mapper = NULL;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+ mapper->addSupportedKeyCode(AKEYCODE_A);
+ mapper->addSupportedKeyCode(AKEYCODE_B);
+
+ const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
+ uint8_t flags[4] = { 0, 0, 0, 1 };
+
+ ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, 4, keyCodes, flags))
+ << "Should return false when device id is >= 0 but unknown.";
+ ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
+
+ flags[3] = 1;
+ ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+ << "Should return false when device id is valid but the sources are not supported by the device.";
+ ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
+
+ flags[3] = 1;
+ ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+ << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+ ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
+
+ flags[3] = 1;
+ ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+ << "Should return false when the device id is < 0 but the sources are not supported by any device.";
+ ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
+
+ flags[3] = 1;
+ ASSERT_TRUE(mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+ << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources.";
+ ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
+}
+
+TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
+ addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD);
+
+ FakeInputDispatcher::NotifyConfigurationChangedArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyConfigurationChangedWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+}
+
+TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
+ FakeInputMapper* mapper = NULL;
+ ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
+ INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+
+ mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, AKEYCODE_A, 1, POLICY_FLAG_WAKE);
+ mReader->loopOnce();
+ ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
+
+ RawEvent event;
+ ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event));
+ ASSERT_EQ(0, event.when);
+ ASSERT_EQ(1, event.deviceId);
+ ASSERT_EQ(EV_KEY, event.type);
+ ASSERT_EQ(KEY_A, event.scanCode);
+ ASSERT_EQ(AKEYCODE_A, event.keyCode);
+ ASSERT_EQ(1, event.value);
+ ASSERT_EQ(POLICY_FLAG_WAKE, event.flags);
+}
+
+
+// --- InputDeviceTest ---
+
+class InputDeviceTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const int32_t DEVICE_ID;
+
+ sp<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ sp<FakeInputDispatcher> mFakeDispatcher;
+ FakeInputReaderContext* mFakeContext;
+
+ InputDevice* mDevice;
+
+ virtual void SetUp() {
+ mFakeEventHub = new FakeEventHub();
+ mFakePolicy = new FakeInputReaderPolicy();
+ mFakeDispatcher = new FakeInputDispatcher();
+ mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+
+ mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+ }
+
+ virtual void TearDown() {
+ delete mDevice;
+
+ delete mFakeContext;
+ mFakeDispatcher.clear();
+ mFakePolicy.clear();
+ mFakeEventHub.clear();
+ }
+};
+
+const char* InputDeviceTest::DEVICE_NAME = "device";
+const int32_t InputDeviceTest::DEVICE_ID = 1;
+
+TEST_F(InputDeviceTest, ImmutableProperties) {
+ ASSERT_EQ(DEVICE_ID, mDevice->getId());
+ ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
+}
+
+TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
+ // Configuration.
+ mDevice->configure();
+
+ // Metadata.
+ ASSERT_TRUE(mDevice->isIgnored());
+ ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mDevice->getSources());
+
+ InputDeviceInfo info;
+ mDevice->getDeviceInfo(&info);
+ ASSERT_EQ(DEVICE_ID, info.getId());
+ ASSERT_STREQ(DEVICE_NAME, info.getName().string());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType());
+ ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources());
+
+ // State queries.
+ ASSERT_EQ(0, mDevice->getMetaState());
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, 0))
+ << "Ignored device should return unknown key code state.";
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 0))
+ << "Ignored device should return unknown scan code state.";
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 0))
+ << "Ignored device should return unknown switch state.";
+
+ const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
+ uint8_t flags[2] = { 0, 1 };
+ ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 2, keyCodes, flags))
+ << "Ignored device should never mark any key codes.";
+ ASSERT_EQ(0, flags[0]) << "Flag for unsupported key should be unchanged.";
+ ASSERT_EQ(1, flags[1]) << "Flag for unsupported key should be unchanged.";
+
+ // Reset.
+ mDevice->reset();
+}
+
+TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
+ // Configuration.
+ InputDeviceCalibration calibration;
+ calibration.addProperty(String8("key"), String8("value"));
+ mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration);
+
+ FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
+ mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ mapper1->setMetaState(AMETA_ALT_ON);
+ mapper1->addSupportedKeyCode(AKEYCODE_A);
+ mapper1->addSupportedKeyCode(AKEYCODE_B);
+ mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+ mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
+ mapper1->setScanCodeState(2, AKEY_STATE_DOWN);
+ mapper1->setScanCodeState(3, AKEY_STATE_UP);
+ mapper1->setSwitchState(4, AKEY_STATE_DOWN);
+ mDevice->addMapper(mapper1);
+
+ FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
+ mapper2->setMetaState(AMETA_SHIFT_ON);
+ mDevice->addMapper(mapper2);
+
+ mDevice->configure();
+
+ String8 propertyValue;
+ ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue))
+ << "Device should have read calibration during configuration phase.";
+ ASSERT_STREQ("value", propertyValue.string());
+
+ ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled());
+
+ // Metadata.
+ ASSERT_FALSE(mDevice->isIgnored());
+ ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), mDevice->getSources());
+
+ InputDeviceInfo info;
+ mDevice->getDeviceInfo(&info);
+ ASSERT_EQ(DEVICE_ID, info.getId());
+ ASSERT_STREQ(DEVICE_NAME, info.getName().string());
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType());
+ ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources());
+
+ // State queries.
+ ASSERT_EQ(AMETA_ALT_ON | AMETA_SHIFT_ON, mDevice->getMetaState())
+ << "Should query mappers and combine meta states.";
+
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return unknown key code state when source not supported.";
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return unknown scan code state when source not supported.";
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+ << "Should return unknown switch state when source not supported.";
+
+ ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, AKEYCODE_A))
+ << "Should query mapper when source is supported.";
+ ASSERT_EQ(AKEY_STATE_UP, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 3))
+ << "Should query mapper when source is supported.";
+ ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 4))
+ << "Should query mapper when source is supported.";
+
+ const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
+ uint8_t flags[4] = { 0, 0, 0, 1 };
+ ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+ << "Should do nothing when source is unsupported.";
+ ASSERT_EQ(0, flags[0]) << "Flag should be unchanged when source is unsupported.";
+ ASSERT_EQ(0, flags[1]) << "Flag should be unchanged when source is unsupported.";
+ ASSERT_EQ(0, flags[2]) << "Flag should be unchanged when source is unsupported.";
+ ASSERT_EQ(1, flags[3]) << "Flag should be unchanged when source is unsupported.";
+
+ ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 4, keyCodes, flags))
+ << "Should query mapper when source is supported.";
+ ASSERT_EQ(1, flags[0]) << "Flag for supported key should be set.";
+ ASSERT_EQ(1, flags[1]) << "Flag for supported key should be set.";
+ ASSERT_EQ(0, flags[2]) << "Flag for unsupported key should be unchanged.";
+ ASSERT_EQ(1, flags[3]) << "Flag for unsupported key should be unchanged.";
+
+ // Event handling.
+ RawEvent event;
+ mDevice->process(&event);
+
+ ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
+
+ // Reset.
+ mDevice->reset();
+
+ ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled());
+}
+
+
+// --- InputMapperTest ---
+
+class InputMapperTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const int32_t DEVICE_ID;
+
+ sp<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ sp<FakeInputDispatcher> mFakeDispatcher;
+ FakeInputReaderContext* mFakeContext;
+ InputDevice* mDevice;
+
+ virtual void SetUp() {
+ mFakeEventHub = new FakeEventHub();
+ mFakePolicy = new FakeInputReaderPolicy();
+ mFakeDispatcher = new FakeInputDispatcher();
+ mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher);
+ mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+
+ mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
+ }
+
+ virtual void TearDown() {
+ delete mDevice;
+ delete mFakeContext;
+ mFakeDispatcher.clear();
+ mFakePolicy.clear();
+ mFakeEventHub.clear();
+ }
+
+ void prepareCalibration(const char* key, const char* value) {
+ mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME),
+ String8(key), String8(value));
+ }
+
+ void addMapperAndConfigure(InputMapper* mapper) {
+ mDevice->addMapper(mapper);
+ mDevice->configure();
+ }
+
+ static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type,
+ int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) {
+ RawEvent event;
+ event.when = when;
+ event.deviceId = deviceId;
+ event.type = type;
+ event.scanCode = scanCode;
+ event.keyCode = keyCode;
+ event.value = value;
+ event.flags = flags;
+ mapper->process(&event);
+ }
+
+ static void assertMotionRange(const InputDeviceInfo& info,
+ int32_t rangeType, float min, float max, float flat, float fuzz) {
+ const InputDeviceInfo::MotionRange* range = info.getMotionRange(rangeType);
+ ASSERT_TRUE(range != NULL) << "Range: " << rangeType;
+ ASSERT_NEAR(min, range->min, EPSILON) << "Range: " << rangeType;
+ ASSERT_NEAR(max, range->max, EPSILON) << "Range: " << rangeType;
+ ASSERT_NEAR(flat, range->flat, EPSILON) << "Range: " << rangeType;
+ ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Range: " << rangeType;
+ }
+
+ static void assertPointerCoords(const PointerCoords& coords,
+ float x, float y, float pressure, float size,
+ float touchMajor, float touchMinor, float toolMajor, float toolMinor,
+ float orientation) {
+ ASSERT_NEAR(x, coords.x, 1);
+ ASSERT_NEAR(y, coords.y, 1);
+ ASSERT_NEAR(pressure, coords.pressure, EPSILON);
+ ASSERT_NEAR(size, coords.size, EPSILON);
+ ASSERT_NEAR(touchMajor, coords.touchMajor, 1);
+ ASSERT_NEAR(touchMinor, coords.touchMinor, 1);
+ ASSERT_NEAR(toolMajor, coords.toolMajor, 1);
+ ASSERT_NEAR(toolMinor, coords.toolMinor, 1);
+ ASSERT_NEAR(orientation, coords.orientation, EPSILON);
+ }
+};
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const int32_t InputMapperTest::DEVICE_ID = 1;
+
+
+// --- SwitchInputMapperTest ---
+
+class SwitchInputMapperTest : public InputMapperTest {
+protected:
+};
+
+TEST_F(SwitchInputMapperTest, GetSources) {
+ SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(uint32_t(0), mapper->getSources());
+}
+
+TEST_F(SwitchInputMapperTest, GetSwitchState) {
+ SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
+ addMapperAndConfigure(mapper);
+
+ mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
+ ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+
+ mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
+ ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+}
+
+TEST_F(SwitchInputMapperTest, Process) {
+ SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
+ addMapperAndConfigure(mapper);
+
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 0, 1, 0);
+
+ FakeInputDispatcher::NotifySwitchArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifySwitchWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.when);
+ ASSERT_EQ(SW_LID, args.switchCode);
+ ASSERT_EQ(1, args.switchValue);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+}
+
+
+// --- KeyboardInputMapperTest ---
+
+class KeyboardInputMapperTest : public InputMapperTest {
+protected:
+ void testDPadKeyRotation(KeyboardInputMapper* mapper,
+ int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
+};
+
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
+ int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
+ FakeInputDispatcher::NotifyKeyArgs args;
+
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(originalScanCode, args.scanCode);
+ ASSERT_EQ(rotatedKeyCode, args.keyCode);
+}
+
+
+TEST_F(KeyboardInputMapperTest, GetSources) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ // Key down.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID,
+ EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE);
+ FakeInputDispatcher::NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Key up.
+ process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
+ EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ // Key down.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID,
+ EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Key up.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID,
+ EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Reset. Since no keys still down, should not synthesize any key ups.
+ mapper->reset();
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ // Metakey down.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID,
+ EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Key down.
+ process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
+ EV_KEY, KEY_A, AKEYCODE_A, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Reset. Since two keys are still down, should synthesize two key ups in reverse order.
+ mapper->reset();
+
+ FakeInputDispatcher::NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_A, args.keyCode);
+ ASSERT_EQ(KEY_A, args.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEYCODE_SHIFT_LEFT, args.keyCode);
+ ASSERT_EQ(KEY_LEFTSHIFT, args.scanCode);
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime);
+
+ // And that's it.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ // Initial metastate.
+ ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+
+ // Metakey down.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID,
+ EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0);
+ FakeInputDispatcher::NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+ ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+
+ // Key down.
+ process(mapper, ARBITRARY_TIME + 1, DEVICE_ID,
+ EV_KEY, KEY_A, AKEYCODE_A, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+
+ // Key up.
+ process(mapper, ARBITRARY_TIME + 2, DEVICE_ID,
+ EV_KEY, KEY_A, AKEYCODE_A, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+
+ // Metakey up.
+ process(mapper, ARBITRARY_TIME + 3, DEVICE_ID,
+ EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AMETA_NONE, args.metaState);
+ ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+ ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+}
+
+TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+}
+
+TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_0);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_90);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_180);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_270);
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
+ ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
+ KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+
+ // Special case: if orientation changes while key is down, we still emit the same keycode
+ // in the key up as we did in the key down.
+ FakeInputDispatcher::NotifyKeyArgs args;
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_270);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_180);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(KEY_UP, args.scanCode);
+ ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode);
+}
+
+TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
+ ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+
+ mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
+ ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+}
+
+TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
+ ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+
+ mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
+ ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+}
+
+TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+ AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ mFakeEventHub->addKey(DEVICE_ID, KEY_A, AKEYCODE_A, 0);
+
+ const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
+ uint8_t flags[2] = { 0, 0 };
+ ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
+ ASSERT_TRUE(flags[0]);
+ ASSERT_FALSE(flags[1]);
+}
+
+
+// --- TrackballInputMapperTest ---
+
+class TrackballInputMapperTest : public InputMapperTest {
+protected:
+ static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
+
+ void testMotionRotation(TrackballInputMapper* mapper,
+ int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+};
+
+const int32_t TrackballInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
+
+void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper,
+ int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, originalX, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, originalY, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
+ float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD,
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(TrackballInputMapperTest, GetSources) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
+}
+
+TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ InputDeviceInfo info;
+ mapper->populateDeviceInfo(&info);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X,
+ -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y,
+ -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+}
+
+TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Button press.
+ // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Button release. Should have same down time.
+ process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(0, args.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(0, args.edgeFlags);
+ ASSERT_EQ(uint32_t(1), args.pointerCount);
+ ASSERT_EQ(0, args.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
+ ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Motion in X but not Y.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ // Motion in Y but not X.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Button press without following sync.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ // Button release without following sync.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Combined X, Y and Button.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ // Move X, Y a bit while pressed.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 2, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, 1, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+
+ // Release Button.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Button press.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+
+ // Button release.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+
+ // Reset. Should not synthesize button up since button is not pressed.
+ mapper->reset();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Button press.
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+
+ // Reset. Should synthesize button up.
+ mapper->reset();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+}
+
+TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
+}
+
+TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) {
+ TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID);
+ addMapperAndConfigure(mapper);
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_0);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_90);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_180);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
+
+ mFakePolicy->setDisplayInfo(DISPLAY_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ InputReaderPolicyInterface::ROTATION_270);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
+}
+
+
+// --- TouchInputMapperTest ---
+
+class TouchInputMapperTest : public InputMapperTest {
+protected:
+ static const int32_t RAW_X_MIN;
+ static const int32_t RAW_X_MAX;
+ static const int32_t RAW_Y_MIN;
+ static const int32_t RAW_Y_MAX;
+ static const int32_t RAW_TOUCH_MIN;
+ static const int32_t RAW_TOUCH_MAX;
+ static const int32_t RAW_TOOL_MIN;
+ static const int32_t RAW_TOOL_MAX;
+ static const int32_t RAW_PRESSURE_MIN;
+ static const int32_t RAW_PRESSURE_MAX;
+ static const int32_t RAW_ORIENTATION_MIN;
+ static const int32_t RAW_ORIENTATION_MAX;
+ static const int32_t RAW_ID_MIN;
+ static const int32_t RAW_ID_MAX;
+ static const float X_PRECISION;
+ static const float Y_PRECISION;
+
+ static const VirtualKeyDefinition VIRTUAL_KEYS[2];
+
+ enum Axes {
+ POSITION = 1 << 0,
+ TOUCH = 1 << 1,
+ TOOL = 1 << 2,
+ PRESSURE = 1 << 3,
+ ORIENTATION = 1 << 4,
+ MINOR = 1 << 5,
+ ID = 1 << 6,
+ };
+
+ void prepareDisplay(int32_t orientation);
+ void prepareVirtualKeys();
+ int32_t toRawX(float displayX);
+ int32_t toRawY(float displayY);
+ float toDisplayX(int32_t rawX);
+ float toDisplayY(int32_t rawY);
+};
+
+const int32_t TouchInputMapperTest::RAW_X_MIN = 25;
+const int32_t TouchInputMapperTest::RAW_X_MAX = 1020;
+const int32_t TouchInputMapperTest::RAW_Y_MIN = 30;
+const int32_t TouchInputMapperTest::RAW_Y_MAX = 1010;
+const int32_t TouchInputMapperTest::RAW_TOUCH_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31;
+const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_TOOL_MAX = 15;
+const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = RAW_TOUCH_MIN;
+const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX;
+const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7;
+const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7;
+const int32_t TouchInputMapperTest::RAW_ID_MIN = 0;
+const int32_t TouchInputMapperTest::RAW_ID_MAX = 9;
+const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH;
+const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT;
+
+const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = {
+ { KEY_HOME, 60, DISPLAY_HEIGHT + 15, 20, 20 },
+ { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
+};
+
+void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
+ mFakePolicy->setDisplayInfo(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
+}
+
+void TouchInputMapperTest::prepareVirtualKeys() {
+ mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]);
+ mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE);
+}
+
+int32_t TouchInputMapperTest::toRawX(float displayX) {
+ return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH + RAW_X_MIN);
+}
+
+int32_t TouchInputMapperTest::toRawY(float displayY) {
+ return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT + RAW_Y_MIN);
+}
+
+float TouchInputMapperTest::toDisplayX(int32_t rawX) {
+ return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN);
+}
+
+float TouchInputMapperTest::toDisplayY(int32_t rawY) {
+ return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN);
+}
+
+
+// --- SingleTouchInputMapperTest ---
+
+class SingleTouchInputMapperTest : public TouchInputMapperTest {
+protected:
+ void prepareAxes(int axes);
+
+ void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
+ void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
+ void processUp(SingleTouchInputMapper* mappery);
+ void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
+ void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
+ void processSync(SingleTouchInputMapper* mapper);
+};
+
+void SingleTouchInputMapperTest::prepareAxes(int axes) {
+ if (axes & POSITION) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+ }
+ if (axes & PRESSURE) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+ }
+ if (axes & TOOL) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+ }
+}
+
+void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 1, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0);
+}
+
+void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0);
+}
+
+void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 0, 0);
+}
+
+void SingleTouchInputMapperTest::processPressure(
+ SingleTouchInputMapper* mapper, int32_t pressure) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, 0, pressure, 0);
+}
+
+void SingleTouchInputMapperTest::processToolMajor(
+ SingleTouchInputMapper* mapper, int32_t toolMajor) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, 0, toolMajor, 0);
+}
+
+void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+}
+
+
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1);
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+}
+
+TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+}
+
+TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ // Unknown key.
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+
+ // Virtual key is down.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+
+ // Virtual key is up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+}
+
+TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ // Unknown key.
+ ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+
+ // Virtual key is down.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+
+ // Virtual key is up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+}
+
+TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A };
+ uint8_t flags[2] = { 0, 0 };
+ ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
+ ASSERT_TRUE(flags[0]);
+ ASSERT_FALSE(flags[1]);
+}
+
+TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) {
+ // Note: Ideally we should send cancels but the implementation is more straightforward
+ // with up and this will only happen if a device is forcibly removed.
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ // Press virtual key.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Reset. Since key is down, synthesize key up.
+ mapper->reset();
+
+ FakeInputDispatcher::NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ //ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+}
+
+TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ // Press virtual key.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Release virtual key.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled());
+
+ // Reset. Since no key is down, nothing happens.
+ mapper->reset();
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyKeyArgs args;
+
+ // Press virtual key.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Release virtual key.
+ processUp(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
+ ASSERT_EQ(DEVICE_ID, args.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
+ ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags);
+ ASSERT_EQ(AKEYCODE_HOME, args.keyCode);
+ ASSERT_EQ(KEY_HOME, args.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
+ ASSERT_EQ(ARBITRARY_TIME, args.downTime);
+
+ // Should not have sent any motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyKeyArgs keyArgs;
+
+ // Press virtual key.
+ int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
+ int32_t y = toRawY(VIRTUAL_KEYS[0].centerY);
+ processDown(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
+ ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags);
+ ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, keyArgs.flags);
+ ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode);
+ ASSERT_EQ(KEY_HOME, keyArgs.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState);
+ ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime);
+
+ // Move out of bounds. This should generate a cancel and a pointer down since we moved
+ // into the display area.
+ y -= 100;
+ processMove(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs));
+ ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, keyArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source);
+ ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags);
+ ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+ ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
+ | AKEY_EVENT_FLAG_CANCELED, keyArgs.flags);
+ ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode);
+ ASSERT_EQ(KEY_HOME, keyArgs.scanCode);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState);
+ ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime);
+
+ FakeInputDispatcher::NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Keep moving out of bounds. Should generate a pointer move.
+ y -= 50;
+ processMove(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Release out of bounds. Should generate a pointer up.
+ processUp(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Should not have sent any more keys or motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyMotionArgs motionArgs;
+
+ // Initially go down out of bounds.
+ int32_t x = -10;
+ int32_t y = -10;
+ processDown(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+
+ // Move into the display area. Should generate a pointer down.
+ x = 50;
+ y = 75;
+ processMove(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Release. Should generate a pointer up.
+ processUp(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Should not have sent any more keys or motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyMotionArgs motionArgs;
+
+ // Down.
+ int32_t x = 100;
+ int32_t y = 125;
+ processDown(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Move.
+ x += 50;
+ y += 75;
+ processMove(mapper, x, y);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Up.
+ processUp(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Should not have sent any more keys or motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_Rotation) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+
+ // Rotation 0.
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(50, args.pointerCoords[0].x, 1);
+ ASSERT_NEAR(75, args.pointerCoords[0].y, 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+
+ // Rotation 90.
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_90);
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(75, args.pointerCoords[0].x, 1);
+ ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+
+ // Rotation 180.
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_180);
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1);
+ ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+
+ // Rotation 270.
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_270);
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1);
+ ASSERT_NEAR(50, args.pointerCoords[0].y, 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
+ SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | PRESSURE | TOOL);
+ addMapperAndConfigure(mapper);
+
+ // These calculations are based on the input device calibration documentation.
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+ int32_t rawPressure = 10;
+ int32_t rawToolMajor = 12;
+
+ float x = toDisplayX(rawX);
+ float y = toDisplayY(rawY);
+ float pressure = float(rawPressure) / RAW_PRESSURE_MAX;
+ float size = float(rawToolMajor) / RAW_TOOL_MAX;
+ float tool = min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * size;
+ float touch = min(tool * pressure, tool);
+
+ processDown(mapper, rawX, rawY);
+ processPressure(mapper, rawPressure);
+ processToolMajor(mapper, rawToolMajor);
+ processSync(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, pressure, size, touch, touch, tool, tool, 0));
+}
+
+
+// --- MultiTouchInputMapperTest ---
+
+class MultiTouchInputMapperTest : public TouchInputMapperTest {
+protected:
+ void prepareAxes(int axes);
+
+ void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y);
+ void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor);
+ void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor);
+ void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor);
+ void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
+ void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
+ void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
+ void processId(MultiTouchInputMapper* mapper, int32_t id);
+ void processMTSync(MultiTouchInputMapper* mapper);
+ void processSync(MultiTouchInputMapper* mapper);
+};
+
+void MultiTouchInputMapperTest::prepareAxes(int axes) {
+ if (axes & POSITION) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+ }
+ if (axes & TOUCH) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+ if (axes & MINOR) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
+ RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+ }
+ }
+ if (axes & TOOL) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+ if (axes & MINOR) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
+ RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0);
+ }
+ }
+ if (axes & ORIENTATION) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_ORIENTATION,
+ RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0);
+ }
+ if (axes & PRESSURE) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_PRESSURE,
+ RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+ }
+ if (axes & ID) {
+ mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
+ RAW_ID_MIN, RAW_ID_MAX, 0, 0);
+ }
+}
+
+void MultiTouchInputMapperTest::processPosition(
+ MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, 0, x, 0);
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, 0, y, 0);
+}
+
+void MultiTouchInputMapperTest::processTouchMajor(
+ MultiTouchInputMapper* mapper, int32_t touchMajor) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, 0, touchMajor, 0);
+}
+
+void MultiTouchInputMapperTest::processTouchMinor(
+ MultiTouchInputMapper* mapper, int32_t touchMinor) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, 0, touchMinor, 0);
+}
+
+void MultiTouchInputMapperTest::processToolMajor(
+ MultiTouchInputMapper* mapper, int32_t toolMajor) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, 0, toolMajor, 0);
+}
+
+void MultiTouchInputMapperTest::processToolMinor(
+ MultiTouchInputMapper* mapper, int32_t toolMinor) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, 0, toolMinor, 0);
+}
+
+void MultiTouchInputMapperTest::processOrientation(
+ MultiTouchInputMapper* mapper, int32_t orientation) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, 0, orientation, 0);
+}
+
+void MultiTouchInputMapperTest::processPressure(
+ MultiTouchInputMapper* mapper, int32_t pressure) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, 0, pressure, 0);
+}
+
+void MultiTouchInputMapperTest::processId(
+ MultiTouchInputMapper* mapper, int32_t id) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, 0, id, 0);
+}
+
+void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0, 0, 0);
+}
+
+void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
+ process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0);
+}
+
+
+TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyMotionArgs motionArgs;
+
+ // Two fingers down at once.
+ int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_EQ(1, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Move.
+ x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
+ processPosition(mapper, x1, y1);
+ processMTSync(mapper);
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_EQ(1, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // First finger up.
+ x2 += 15; y2 -= 20;
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_EQ(1, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Move.
+ x2 += 20; y2 -= 25;
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // New finger down.
+ int32_t x3 = 700, y3 = 300;
+ processPosition(mapper, x2, y2);
+ processMTSync(mapper);
+ processPosition(mapper, x3, y3);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_EQ(1, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Second finger up.
+ x3 += 30; y3 -= 20;
+ processPosition(mapper, x3, y3);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_EQ(1, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Last finger up.
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+ ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+ ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(0, motionArgs.flags);
+ ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+ ASSERT_EQ(0, motionArgs.edgeFlags);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(0, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON);
+ ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON);
+ ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+ // Should not have sent any more keys or motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | ID);
+ prepareVirtualKeys();
+ addMapperAndConfigure(mapper);
+
+ mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+ FakeInputDispatcher::NotifyMotionArgs motionArgs;
+
+ // Two fingers down at once.
+ int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
+ processPosition(mapper, x1, y1);
+ processId(mapper, 1);
+ processMTSync(mapper);
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_EQ(2, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+
+ // Move.
+ x1 += 10; y1 += 15; x2 += 5; y2 -= 10;
+ processPosition(mapper, x1, y1);
+ processId(mapper, 1);
+ processMTSync(mapper);
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_EQ(2, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+
+ // First finger up.
+ x2 += 15; y2 -= 20;
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(1, motionArgs.pointerIds[0]);
+ ASSERT_EQ(2, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(2, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+
+ // Move.
+ x2 += 20; y2 -= 25;
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(2, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+
+ // New finger down.
+ int32_t x3 = 700, y3 = 300;
+ processPosition(mapper, x2, y2);
+ processId(mapper, 2);
+ processMTSync(mapper);
+ processPosition(mapper, x3, y3);
+ processId(mapper, 3);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(2, motionArgs.pointerIds[0]);
+ ASSERT_EQ(3, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+
+ // Second finger up.
+ x3 += 30; y3 -= 20;
+ processPosition(mapper, x3, y3);
+ processId(mapper, 3);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ motionArgs.action);
+ ASSERT_EQ(size_t(2), motionArgs.pointerCount);
+ ASSERT_EQ(2, motionArgs.pointerIds[0]);
+ ASSERT_EQ(3, motionArgs.pointerIds[1]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(3, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+
+ // Last finger up.
+ processMTSync(mapper);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_EQ(3, motionArgs.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+ toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+
+ // Should not have sent any more keys or motions.
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR);
+ addMapperAndConfigure(mapper);
+
+ // These calculations are based on the input device calibration documentation.
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+ int32_t rawTouchMajor = 7;
+ int32_t rawTouchMinor = 6;
+ int32_t rawToolMajor = 9;
+ int32_t rawToolMinor = 8;
+ int32_t rawPressure = 11;
+ int32_t rawOrientation = 3;
+ int32_t id = 5;
+
+ float x = toDisplayX(rawX);
+ float y = toDisplayY(rawY);
+ float pressure = float(rawPressure) / RAW_PRESSURE_MAX;
+ float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX;
+ float toolMajor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMajor / RAW_TOOL_MAX;
+ float toolMinor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMinor / RAW_TOOL_MAX;
+ float touchMajor = min(toolMajor * pressure, toolMajor);
+ float touchMinor = min(toolMinor * pressure, toolMinor);
+ float orientation = float(rawOrientation) / RAW_ORIENTATION_MAX * M_PI_2;
+
+ processPosition(mapper, rawX, rawY);
+ processTouchMajor(mapper, rawTouchMajor);
+ processTouchMinor(mapper, rawTouchMinor);
+ processToolMajor(mapper, rawToolMajor);
+ processToolMinor(mapper, rawToolMinor);
+ processPressure(mapper, rawPressure);
+ processOrientation(mapper, rawOrientation);
+ processId(mapper, id);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(id, args.pointerIds[0]);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation));
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | TOUCH | TOOL | MINOR);
+ prepareCalibration("touch.touchSize.calibration", "geometric");
+ prepareCalibration("touch.toolSize.calibration", "geometric");
+ addMapperAndConfigure(mapper);
+
+ // These calculations are based on the input device calibration documentation.
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+ int32_t rawTouchMajor = 140;
+ int32_t rawTouchMinor = 120;
+ int32_t rawToolMajor = 180;
+ int32_t rawToolMinor = 160;
+
+ float x = toDisplayX(rawX);
+ float y = toDisplayY(rawY);
+ float pressure = float(rawTouchMajor) / RAW_TOUCH_MAX;
+ float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX;
+ float scale = avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN),
+ float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN));
+ float toolMajor = float(rawToolMajor) * scale;
+ float toolMinor = float(rawToolMinor) * scale;
+ float touchMajor = min(float(rawTouchMajor) * scale, toolMajor);
+ float touchMinor = min(float(rawTouchMinor) * scale, toolMinor);
+
+ processPosition(mapper, rawX, rawY);
+ processTouchMajor(mapper, rawTouchMajor);
+ processTouchMinor(mapper, rawTouchMinor);
+ processToolMajor(mapper, rawToolMajor);
+ processToolMinor(mapper, rawToolMinor);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | TOUCH | TOOL);
+ prepareCalibration("touch.touchSize.calibration", "pressure");
+ prepareCalibration("touch.toolSize.calibration", "linear");
+ prepareCalibration("touch.toolSize.linearScale", "10");
+ prepareCalibration("touch.toolSize.linearBias", "160");
+ prepareCalibration("touch.toolSize.isSummed", "1");
+ prepareCalibration("touch.pressure.calibration", "amplitude");
+ prepareCalibration("touch.pressure.source", "touch");
+ prepareCalibration("touch.pressure.scale", "0.01");
+ addMapperAndConfigure(mapper);
+
+ // These calculations are based on the input device calibration documentation.
+ // Note: We only provide a single common touch/tool value because the device is assumed
+ // not to emit separate values for each pointer (isSummed = 1).
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+ int32_t rawX2 = 150;
+ int32_t rawY2 = 250;
+ int32_t rawTouchMajor = 60;
+ int32_t rawToolMajor = 5;
+
+ float x = toDisplayX(rawX);
+ float y = toDisplayY(rawY);
+ float x2 = toDisplayX(rawX2);
+ float y2 = toDisplayY(rawY2);
+ float pressure = float(rawTouchMajor) * 0.01f;
+ float size = float(rawToolMajor) / RAW_TOOL_MAX;
+ float tool = (float(rawToolMajor) * 10.0f + 160.0f) / 2;
+ float touch = min(tool * pressure, tool);
+
+ processPosition(mapper, rawX, rawY);
+ processTouchMajor(mapper, rawTouchMajor);
+ processToolMajor(mapper, rawToolMajor);
+ processMTSync(mapper);
+ processPosition(mapper, rawX2, rawY2);
+ processTouchMajor(mapper, rawTouchMajor);
+ processToolMajor(mapper, rawToolMajor);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+ ASSERT_EQ(size_t(2), args.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, pressure, size, touch, touch, tool, tool, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1],
+ x2, y2, pressure, size, touch, touch, tool, tool, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID);
+ prepareDisplay(InputReaderPolicyInterface::ROTATION_0);
+ prepareAxes(POSITION | TOUCH | TOOL);
+ prepareCalibration("touch.touchSize.calibration", "pressure");
+ prepareCalibration("touch.toolSize.calibration", "area");
+ prepareCalibration("touch.toolSize.areaScale", "22");
+ prepareCalibration("touch.toolSize.areaBias", "1");
+ prepareCalibration("touch.toolSize.linearScale", "9.2");
+ prepareCalibration("touch.toolSize.linearBias", "3");
+ prepareCalibration("touch.pressure.calibration", "amplitude");
+ prepareCalibration("touch.pressure.source", "touch");
+ prepareCalibration("touch.pressure.scale", "0.01");
+ addMapperAndConfigure(mapper);
+
+ // These calculations are based on the input device calibration documentation.
+ int32_t rawX = 100;
+ int32_t rawY = 200;
+ int32_t rawTouchMajor = 60;
+ int32_t rawToolMajor = 5;
+
+ float x = toDisplayX(rawX);
+ float y = toDisplayY(rawY);
+ float pressure = float(rawTouchMajor) * 0.01f;
+ float size = float(rawToolMajor) / RAW_TOOL_MAX;
+ float tool = sqrtf(float(rawToolMajor) * 22.0f + 1.0f) * 9.2f + 3.0f;
+ float touch = min(tool * pressure, tool);
+
+ processPosition(mapper, rawX, rawY);
+ processTouchMajor(mapper, rawTouchMajor);
+ processToolMajor(mapper, rawToolMajor);
+ processMTSync(mapper);
+ processSync(mapper);
+
+ FakeInputDispatcher::NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+ x, y, pressure, size, touch, touch, tool, tool, 0));
+}
+
+} // namespace android
diff --git a/libs/surfaceflinger/tests/overlays/Android.mk b/libs/ui/tests/region/Android.mk
index 592b601..6cc4a5a 100644
--- a/libs/surfaceflinger/tests/overlays/Android.mk
+++ b/libs/ui/tests/region/Android.mk
@@ -2,15 +2,14 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- overlays.cpp
+ region.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
- libui \
- libsurfaceflinger_client
+ libui
-LOCAL_MODULE:= test-overlays
+LOCAL_MODULE:= test-region
LOCAL_MODULE_TAGS := tests
diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region/region.cpp
index ef15de9..ef15de9 100644
--- a/libs/ui/tests/region.cpp
+++ b/libs/ui/tests/region/region.cpp
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 24cb9a2..d077fec 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -27,11 +27,14 @@ commonSources:= \
FileMap.cpp \
FileLock.cpp \
Flattenable.cpp \
+ ObbFile.cpp \
+ Pool.cpp \
RefBase.cpp \
ResourceTypes.cpp \
SharedBuffer.cpp \
Static.cpp \
StopWatch.cpp \
+ StreamingZipInflater.cpp \
String8.cpp \
String16.cpp \
StringArray.cpp \
@@ -40,7 +43,7 @@ commonSources:= \
Threads.cpp \
Timers.cpp \
VectorImpl.cpp \
- ZipFileCRO.cpp \
+ ZipFileCRO.cpp \
ZipFileRO.cpp \
ZipUtils.cpp \
../../tools/aapt/ZipFile.cpp \
@@ -67,6 +70,11 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1
endif
endif
+ifeq ($(HOST_OS),darwin)
+# MacOS doesn't have lseek64. However, off_t is 64-bit anyway.
+LOCAL_CFLAGS += -DOFF_T_IS_64_BIT
+endif
+
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -79,8 +87,9 @@ include $(CLEAR_VARS)
# we have the common sources, plus some device-specific stuff
LOCAL_SRC_FILES:= \
$(commonSources) \
- BackupData.cpp \
- BackupHelpers.cpp
+ BackupData.cpp \
+ BackupHelpers.cpp \
+ Looper.cpp
ifeq ($(TARGET_OS),linux)
LOCAL_LDLIBS += -lrt -ldl
@@ -117,3 +126,13 @@ LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp
include $(BUILD_STATIC_LIBRARY)
endif
endif
+
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
index 4295123..cef7db4 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/utils/Asset.cpp
@@ -24,6 +24,7 @@
#include <utils/Asset.h>
#include <utils/Atomic.h>
#include <utils/FileMap.h>
+#include <utils/StreamingZipInflater.h>
#include <utils/ZipUtils.h>
#include <utils/ZipFileRO.h>
#include <utils/Log.h>
@@ -659,7 +660,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map)
*/
_CompressedAsset::_CompressedAsset(void)
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
- mMap(NULL), mFd(-1), mBuf(NULL)
+ mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
{
}
@@ -698,6 +699,10 @@ status_t _CompressedAsset::openChunk(int fd, off_t offset,
mFd = fd;
assert(mBuf == NULL);
+ if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
+ mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
+ }
+
return NO_ERROR;
}
@@ -724,6 +729,9 @@ status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
mUncompressedLen = uncompressedLen;
assert(mOffset == 0);
+ if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
+ mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
+ }
return NO_ERROR;
}
@@ -739,26 +747,29 @@ ssize_t _CompressedAsset::read(void* buf, size_t count)
assert(mOffset >= 0 && mOffset <= mUncompressedLen);
- // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly
+ /* If we're relying on a streaming inflater, go through that */
+ if (mZipInflater) {
+ actual = mZipInflater->read(buf, count);
+ } else {
+ if (mBuf == NULL) {
+ if (getBuffer(false) == NULL)
+ return -1;
+ }
+ assert(mBuf != NULL);
- if (mBuf == NULL) {
- if (getBuffer(false) == NULL)
- return -1;
- }
- assert(mBuf != NULL);
+ /* adjust count if we're near EOF */
+ maxLen = mUncompressedLen - mOffset;
+ if (count > maxLen)
+ count = maxLen;
- /* adjust count if we're near EOF */
- maxLen = mUncompressedLen - mOffset;
- if (count > maxLen)
- count = maxLen;
+ if (!count)
+ return 0;
- if (!count)
- return 0;
-
- /* copy from buffer */
- //printf("comp buf read\n");
- memcpy(buf, (char*)mBuf + mOffset, count);
- actual = count;
+ /* copy from buffer */
+ //printf("comp buf read\n");
+ memcpy(buf, (char*)mBuf + mOffset, count);
+ actual = count;
+ }
mOffset += actual;
return actual;
@@ -780,6 +791,9 @@ off_t _CompressedAsset::seek(off_t offset, int whence)
if (newPosn == (off_t) -1)
return newPosn;
+ if (mZipInflater) {
+ mZipInflater->seekAbsolute(newPosn);
+ }
mOffset = newPosn;
return mOffset;
}
@@ -793,10 +807,12 @@ void _CompressedAsset::close(void)
mMap->release();
mMap = NULL;
}
- if (mBuf != NULL) {
- delete[] mBuf;
- mBuf = NULL;
- }
+
+ delete[] mBuf;
+ mBuf = NULL;
+
+ delete mZipInflater;
+ mZipInflater = NULL;
if (mFd > 0) {
::close(mFd);
@@ -817,12 +833,6 @@ const void* _CompressedAsset::getBuffer(bool wordAligned)
if (mBuf != NULL)
return mBuf;
- if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
- LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
- (long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
- goto bail;
- }
-
/*
* Allocate a buffer and read the file into it.
*/
@@ -853,7 +863,13 @@ const void* _CompressedAsset::getBuffer(bool wordAligned)
goto bail;
}
- /* success! */
+ /*
+ * Success - now that we have the full asset in RAM we
+ * no longer need the streaming inflater
+ */
+ delete mZipInflater;
+ mZipInflater = NULL;
+
mBuf = buf;
buf = NULL;
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index eb8e62e..e0d83b4 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -245,6 +245,12 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l
}
}
+void AssetManager::getConfiguration(ResTable_config* outConfig) const
+{
+ AutoMutex _l(mLock);
+ *outConfig = *mConfig;
+}
+
/*
* Open an asset.
*
@@ -1254,7 +1260,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
// TODO: look for previously-created shared memory slice?
int method;
- long uncompressedLen;
+ size_t uncompressedLen;
//printf("USING Zip '%s'\n", pEntry->getFileName());
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
new file mode 100644
index 0000000..a5363d6
--- /dev/null
+++ b/libs/utils/Looper.cpp
@@ -0,0 +1,596 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// A looper implementation based on epoll().
+//
+#define LOG_TAG "Looper"
+
+//#define LOG_NDEBUG 0
+
+// Debugs poll and wake interactions.
+#define DEBUG_POLL_AND_WAKE 0
+
+// Debugs callback registration and invocation.
+#define DEBUG_CALLBACKS 0
+
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+
+namespace android {
+
+#ifdef LOOPER_USES_EPOLL
+// Hint for number of file descriptors to be associated with the epoll instance.
+static const int EPOLL_SIZE_HINT = 8;
+
+// Maximum number of file descriptors for which to retrieve poll events each iteration.
+static const int EPOLL_MAX_EVENTS = 16;
+#endif
+
+static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
+static pthread_key_t gTLSKey = 0;
+
+Looper::Looper(bool allowNonCallbacks) :
+ mAllowNonCallbacks(allowNonCallbacks),
+ mResponseIndex(0) {
+ int wakeFds[2];
+ int result = pipe(wakeFds);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
+
+ mWakeReadPipeFd = wakeFds[0];
+ mWakeWritePipeFd = wakeFds[1];
+
+ result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
+ errno);
+
+ result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
+ errno);
+
+#ifdef LOOPER_USES_EPOLL
+ // Allocate the epoll instance and register the wake pipe.
+ mEpollFd = epoll_create(EPOLL_SIZE_HINT);
+ LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
+
+ struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem.events = EPOLLIN;
+ eventItem.data.fd = mWakeReadPipeFd;
+ result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
+ errno);
+#else
+ // Add the wake pipe to the head of the request list with a null callback.
+ struct pollfd requestedFd;
+ requestedFd.fd = mWakeReadPipeFd;
+ requestedFd.events = POLLIN;
+ mRequestedFds.push(requestedFd);
+
+ Request request;
+ request.fd = mWakeReadPipeFd;
+ request.callback = NULL;
+ request.ident = 0;
+ request.data = NULL;
+ mRequests.push(request);
+
+ mPolling = false;
+ mWaiters = 0;
+#endif
+
+#ifdef LOOPER_STATISTICS
+ mPendingWakeTime = -1;
+ mPendingWakeCount = 0;
+ mSampledWakeCycles = 0;
+ mSampledWakeCountSum = 0;
+ mSampledWakeLatencySum = 0;
+
+ mSampledPolls = 0;
+ mSampledZeroPollCount = 0;
+ mSampledZeroPollLatencySum = 0;
+ mSampledTimeoutPollCount = 0;
+ mSampledTimeoutPollLatencySum = 0;
+#endif
+}
+
+Looper::~Looper() {
+ close(mWakeReadPipeFd);
+ close(mWakeWritePipeFd);
+#ifdef LOOPER_USES_EPOLL
+ close(mEpollFd);
+#endif
+}
+
+void Looper::initTLSKey() {
+ int result = pthread_key_create(& gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+}
+
+void Looper::threadDestructor(void *st) {
+ Looper* const self = static_cast<Looper*>(st);
+ if (self != NULL) {
+ self->decStrong((void*)threadDestructor);
+ }
+}
+
+void Looper::setForThread(const sp<Looper>& looper) {
+ sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
+
+ if (looper != NULL) {
+ looper->incStrong((void*)threadDestructor);
+ }
+
+ pthread_setspecific(gTLSKey, looper.get());
+
+ if (old != NULL) {
+ old->decStrong((void*)threadDestructor);
+ }
+}
+
+sp<Looper> Looper::getForThread() {
+ int result = pthread_once(& gTLSOnce, initTLSKey);
+ LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
+
+ return (Looper*)pthread_getspecific(gTLSKey);
+}
+
+sp<Looper> Looper::prepare(int opts) {
+ bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS;
+ sp<Looper> looper = Looper::getForThread();
+ if (looper == NULL) {
+ looper = new Looper(allowNonCallbacks);
+ Looper::setForThread(looper);
+ }
+ if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
+ LOGW("Looper already prepared for this thread with a different value for the "
+ "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option.");
+ }
+ return looper;
+}
+
+bool Looper::getAllowNonCallbacks() const {
+ return mAllowNonCallbacks;
+}
+
+int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+ int result = 0;
+ for (;;) {
+ while (mResponseIndex < mResponses.size()) {
+ const Response& response = mResponses.itemAt(mResponseIndex++);
+ if (! response.request.callback) {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - returning signalled identifier %d: "
+ "fd=%d, events=0x%x, data=%p", this,
+ response.request.ident, response.request.fd,
+ response.events, response.request.data);
+#endif
+ if (outFd != NULL) *outFd = response.request.fd;
+ if (outEvents != NULL) *outEvents = response.events;
+ if (outData != NULL) *outData = response.request.data;
+ return response.request.ident;
+ }
+ }
+
+ if (result != 0) {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - returning result %d", this, result);
+#endif
+ if (outFd != NULL) *outFd = 0;
+ if (outEvents != NULL) *outEvents = NULL;
+ if (outData != NULL) *outData = NULL;
+ return result;
+ }
+
+ result = pollInner(timeoutMillis);
+ }
+}
+
+int Looper::pollInner(int timeoutMillis) {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
+#endif
+
+ int result = ALOOPER_POLL_WAKE;
+ mResponses.clear();
+ mResponseIndex = 0;
+
+#ifdef LOOPER_STATISTICS
+ nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+
+#ifdef LOOPER_USES_EPOLL
+ struct epoll_event eventItems[EPOLL_MAX_EVENTS];
+ int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
+ bool acquiredLock = false;
+#else
+ // Wait for wakeAndLock() waiters to run then set mPolling to true.
+ mLock.lock();
+ while (mWaiters != 0) {
+ mResume.wait(mLock);
+ }
+ mPolling = true;
+ mLock.unlock();
+
+ size_t requestedCount = mRequestedFds.size();
+ int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
+#endif
+
+ if (eventCount < 0) {
+ if (errno == EINTR) {
+ goto Done;
+ }
+
+ LOGW("Poll failed with an unexpected error, errno=%d", errno);
+ result = ALOOPER_POLL_ERROR;
+ goto Done;
+ }
+
+ if (eventCount == 0) {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - timeout", this);
+#endif
+ result = ALOOPER_POLL_TIMEOUT;
+ goto Done;
+ }
+
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
+#endif
+
+#ifdef LOOPER_USES_EPOLL
+ for (int i = 0; i < eventCount; i++) {
+ int fd = eventItems[i].data.fd;
+ uint32_t epollEvents = eventItems[i].events;
+ if (fd == mWakeReadPipeFd) {
+ if (epollEvents & EPOLLIN) {
+ awoken();
+ } else {
+ LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
+ }
+ } else {
+ if (! acquiredLock) {
+ mLock.lock();
+ acquiredLock = true;
+ }
+
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex >= 0) {
+ int events = 0;
+ if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
+ if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+ if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
+ if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;
+ pushResponse(events, mRequests.valueAt(requestIndex));
+ } else {
+ LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
+ "no longer registered.", epollEvents, fd);
+ }
+ }
+ }
+ if (acquiredLock) {
+ mLock.unlock();
+ }
+Done: ;
+#else
+ for (size_t i = 0; i < requestedCount; i++) {
+ const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
+
+ short pollEvents = requestedFd.revents;
+ if (pollEvents) {
+ if (requestedFd.fd == mWakeReadPipeFd) {
+ if (pollEvents & POLLIN) {
+ awoken();
+ } else {
+ LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents);
+ }
+ } else {
+ int events = 0;
+ if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT;
+ if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT;
+ if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR;
+ if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP;
+ if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID;
+ pushResponse(events, mRequests.itemAt(i));
+ }
+ if (--eventCount == 0) {
+ break;
+ }
+ }
+ }
+
+Done:
+ // Set mPolling to false and wake up the wakeAndLock() waiters.
+ mLock.lock();
+ mPolling = false;
+ if (mWaiters != 0) {
+ mAwake.broadcast();
+ }
+ mLock.unlock();
+#endif
+
+#ifdef LOOPER_STATISTICS
+ nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mSampledPolls += 1;
+ if (timeoutMillis == 0) {
+ mSampledZeroPollCount += 1;
+ mSampledZeroPollLatencySum += pollEndTime - pollStartTime;
+ } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) {
+ mSampledTimeoutPollCount += 1;
+ mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime
+ - milliseconds_to_nanoseconds(timeoutMillis);
+ }
+ if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) {
+ LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this,
+ 0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount,
+ 0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount);
+ mSampledPolls = 0;
+ mSampledZeroPollCount = 0;
+ mSampledZeroPollLatencySum = 0;
+ mSampledTimeoutPollCount = 0;
+ mSampledTimeoutPollLatencySum = 0;
+ }
+#endif
+
+ for (size_t i = 0; i < mResponses.size(); i++) {
+ const Response& response = mResponses.itemAt(i);
+ if (response.request.callback) {
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+ LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
+ response.request.fd, response.events, response.request.data);
+#endif
+ int callbackResult = response.request.callback(
+ response.request.fd, response.events, response.request.data);
+ if (callbackResult == 0) {
+ removeFd(response.request.fd);
+ }
+
+ result = ALOOPER_POLL_CALLBACK;
+ }
+ }
+ return result;
+}
+
+int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
+ if (timeoutMillis <= 0) {
+ int result;
+ do {
+ result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+ } while (result == ALOOPER_POLL_CALLBACK);
+ return result;
+ } else {
+ nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)
+ + milliseconds_to_nanoseconds(timeoutMillis);
+
+ for (;;) {
+ int result = pollOnce(timeoutMillis, outFd, outEvents, outData);
+ if (result != ALOOPER_POLL_CALLBACK) {
+ return result;
+ }
+
+ nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC);
+ if (timeoutNanos <= 0) {
+ return ALOOPER_POLL_TIMEOUT;
+ }
+
+ timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL));
+ }
+ }
+}
+
+void Looper::wake() {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ wake", this);
+#endif
+
+#ifdef LOOPER_STATISTICS
+ // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled.
+ if (mPendingWakeCount++ == 0) {
+ mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ }
+#endif
+
+ ssize_t nWrite;
+ do {
+ nWrite = write(mWakeWritePipeFd, "W", 1);
+ } while (nWrite == -1 && errno == EINTR);
+
+ if (nWrite != 1) {
+ if (errno != EAGAIN) {
+ LOGW("Could not write wake signal, errno=%d", errno);
+ }
+ }
+}
+
+void Looper::awoken() {
+#if DEBUG_POLL_AND_WAKE
+ LOGD("%p ~ awoken", this);
+#endif
+
+#ifdef LOOPER_STATISTICS
+ if (mPendingWakeCount == 0) {
+ LOGD("%p ~ awoken: spurious!", this);
+ } else {
+ mSampledWakeCycles += 1;
+ mSampledWakeCountSum += mPendingWakeCount;
+ mSampledWakeLatencySum += systemTime(SYSTEM_TIME_MONOTONIC) - mPendingWakeTime;
+ mPendingWakeCount = 0;
+ mPendingWakeTime = -1;
+ if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) {
+ LOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this,
+ 0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles,
+ float(mSampledWakeCountSum) / mSampledWakeCycles);
+ mSampledWakeCycles = 0;
+ mSampledWakeCountSum = 0;
+ mSampledWakeLatencySum = 0;
+ }
+ }
+#endif
+
+ char buffer[16];
+ ssize_t nRead;
+ do {
+ nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
+ } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
+}
+
+void Looper::pushResponse(int events, const Request& request) {
+ Response response;
+ response.events = events;
+ response.request = request;
+ mResponses.push(response);
+}
+
+int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
+ events, callback, data);
+#endif
+
+ if (! callback) {
+ if (! mAllowNonCallbacks) {
+ LOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
+ return -1;
+ }
+
+ if (ident < 0) {
+ LOGE("Invalid attempt to set NULL callback with ident <= 0.");
+ return -1;
+ }
+ }
+
+#ifdef LOOPER_USES_EPOLL
+ int epollEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
+ if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ Request request;
+ request.fd = fd;
+ request.ident = ident;
+ request.callback = callback;
+ request.data = data;
+
+ struct epoll_event eventItem;
+ memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+ eventItem.events = epollEvents;
+ eventItem.data.fd = fd;
+
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex < 0) {
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
+ if (epollResult < 0) {
+ LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
+ return -1;
+ }
+ mRequests.add(fd, request);
+ } else {
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
+ if (epollResult < 0) {
+ LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
+ return -1;
+ }
+ mRequests.replaceValueAt(requestIndex, request);
+ }
+ } // release lock
+#else
+ int pollEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) pollEvents |= POLLIN;
+ if (events & ALOOPER_EVENT_OUTPUT) pollEvents |= POLLOUT;
+
+ wakeAndLock(); // acquire lock
+
+ struct pollfd requestedFd;
+ requestedFd.fd = fd;
+ requestedFd.events = pollEvents;
+
+ Request request;
+ request.fd = fd;
+ request.ident = ident;
+ request.callback = callback;
+ request.data = data;
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index < 0) {
+ mRequestedFds.push(requestedFd);
+ mRequests.push(request);
+ } else {
+ mRequestedFds.replaceAt(requestedFd, size_t(index));
+ mRequests.replaceAt(request, size_t(index));
+ }
+
+ mLock.unlock(); // release lock
+#endif
+ return 1;
+}
+
+int Looper::removeFd(int fd) {
+#if DEBUG_CALLBACKS
+ LOGD("%p ~ removeFd - fd=%d", this, fd);
+#endif
+
+#ifdef LOOPER_USES_EPOLL
+ { // acquire lock
+ AutoMutex _l(mLock);
+ ssize_t requestIndex = mRequests.indexOfKey(fd);
+ if (requestIndex < 0) {
+ return 0;
+ }
+
+ int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
+ if (epollResult < 0) {
+ LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno);
+ return -1;
+ }
+
+ mRequests.removeItemsAt(requestIndex);
+ } // release lock
+ return 1;
+#else
+ wakeAndLock(); // acquire lock
+
+ ssize_t index = getRequestIndexLocked(fd);
+ if (index >= 0) {
+ mRequestedFds.removeAt(size_t(index));
+ mRequests.removeAt(size_t(index));
+ }
+
+ mLock.unlock(); // release lock
+ return index >= 0;
+#endif
+}
+
+#ifndef LOOPER_USES_EPOLL
+ssize_t Looper::getRequestIndexLocked(int fd) {
+ size_t requestCount = mRequestedFds.size();
+
+ for (size_t i = 0; i < requestCount; i++) {
+ if (mRequestedFds.itemAt(i).fd == fd) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+void Looper::wakeAndLock() {
+ mLock.lock();
+
+ mWaiters += 1;
+ while (mPolling) {
+ wake();
+ mAwake.wait(mLock);
+ }
+
+ mWaiters -= 1;
+ if (mWaiters == 0) {
+ mResume.signal();
+ }
+}
+#endif
+
+} // namespace android
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
new file mode 100644
index 0000000..2c3724c
--- /dev/null
+++ b/libs/utils/ObbFile.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2010 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LOG_TAG "ObbFile"
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+
+//#define DEBUG 1
+
+#define kFooterTagSize 8 /* last two 32-bit integers */
+
+#define kFooterMinSize 33 /* 32-bit signature version (4 bytes)
+ * 32-bit package version (4 bytes)
+ * 32-bit flags (4 bytes)
+ * 64-bit salt (8 bytes)
+ * 32-bit package name size (4 bytes)
+ * >=1-character package name (1 byte)
+ * 32-bit footer size (4 bytes)
+ * 32-bit footer marker (4 bytes)
+ */
+
+#define kMaxBufSize 32768 /* Maximum file read buffer */
+
+#define kSignature 0x01059983U /* ObbFile signature */
+
+#define kSigVersion 1 /* We only know about signature version 1 */
+
+/* offsets in version 1 of the header */
+#define kPackageVersionOffset 4
+#define kFlagsOffset 8
+#define kSaltOffset 12
+#define kPackageNameLenOffset 20
+#define kPackageNameOffset 24
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+/*
+ * Work around situations where off_t is 64-bit and use off64_t in
+ * situations where it's 32-bit.
+ */
+#ifdef OFF_T_IS_64_BIT
+#define my_lseek64 lseek
+typedef off_t my_off64_t;
+#else
+#define my_lseek64 lseek64
+typedef off64_t my_off64_t;
+#endif
+
+namespace android {
+
+ObbFile::ObbFile()
+ : mPackageName("")
+ , mVersion(-1)
+ , mFlags(0)
+{
+ memset(mSalt, 0, sizeof(mSalt));
+}
+
+ObbFile::~ObbFile() {
+}
+
+bool ObbFile::readFrom(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_RDONLY);
+ if (fd < 0) {
+ LOGW("couldn't open file %s: %s", filename, strerror(errno));
+ goto out;
+ }
+ success = readFrom(fd);
+ close(fd);
+
+ if (!success) {
+ LOGW("failed to read from %s (fd=%d)\n", filename, fd);
+ }
+
+out:
+ return success;
+}
+
+bool ObbFile::readFrom(int fd)
+{
+ if (fd < 0) {
+ LOGW("attempt to read from invalid fd\n");
+ return false;
+ }
+
+ return parseObbFile(fd);
+}
+
+bool ObbFile::parseObbFile(int fd)
+{
+ my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END);
+
+ if (fileLength < kFooterMinSize) {
+ if (fileLength < 0) {
+ LOGW("error seeking in ObbFile: %s\n", strerror(errno));
+ } else {
+ LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
+ }
+ return false;
+ }
+
+ ssize_t actual;
+ size_t footerSize;
+
+ {
+ my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET);
+
+ char *footer = new char[kFooterTagSize];
+ actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize));
+ if (actual != kFooterTagSize) {
+ LOGW("couldn't read footer signature: %s\n", strerror(errno));
+ return false;
+ }
+
+ unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t));
+ if (fileSig != kSignature) {
+ LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n",
+ kSignature, fileSig);
+ return false;
+ }
+
+ footerSize = get4LE((unsigned char*)footer);
+ if (footerSize > (size_t)fileLength - kFooterTagSize
+ || footerSize > kMaxBufSize) {
+ LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n",
+ footerSize, fileLength);
+ return false;
+ }
+
+ if (footerSize < (kFooterMinSize - kFooterTagSize)) {
+ LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize - kFooterTagSize);
+ return false;
+ }
+ }
+
+ my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
+ if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
+ LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
+ return false;
+ }
+
+ mFooterStart = fileOffset;
+
+ char* scanBuf = (char*)malloc(footerSize);
+ if (scanBuf == NULL) {
+ LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
+ return false;
+ }
+
+ actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize));
+ // readAmount is guaranteed to be less than kMaxBufSize
+ if (actual != (ssize_t)footerSize) {
+ LOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+#ifdef DEBUG
+ for (int i = 0; i < footerSize; ++i) {
+ LOGI("char: 0x%02x\n", scanBuf[i]);
+ }
+#endif
+
+ uint32_t sigVersion = get4LE((unsigned char*)scanBuf);
+ if (sigVersion != kSigVersion) {
+ LOGW("Unsupported ObbFile version %d\n", sigVersion);
+ free(scanBuf);
+ return false;
+ }
+
+ mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
+ mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
+
+ memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt));
+
+ uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
+ if (packageNameLen <= 0
+ || packageNameLen > (footerSize - kPackageNameOffset)) {
+ LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n",
+ packageNameLen, footerSize - kPackageNameOffset);
+ free(scanBuf);
+ return false;
+ }
+
+ char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
+ mPackageName = String8(const_cast<char*>(packageName), packageNameLen);
+
+ free(scanBuf);
+
+#ifdef DEBUG
+ LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion);
+#endif
+
+ return true;
+}
+
+bool ObbFile::writeTo(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_WRONLY);
+ if (fd < 0) {
+ goto out;
+ }
+ success = writeTo(fd);
+ close(fd);
+
+out:
+ if (!success) {
+ LOGW("failed to write to %s: %s\n", filename, strerror(errno));
+ }
+ return success;
+}
+
+bool ObbFile::writeTo(int fd)
+{
+ if (fd < 0) {
+ return false;
+ }
+
+ my_lseek64(fd, 0, SEEK_END);
+
+ if (mPackageName.size() == 0 || mVersion == -1) {
+ LOGW("tried to write uninitialized ObbFile data\n");
+ return false;
+ }
+
+ unsigned char intBuf[sizeof(uint32_t)+1];
+ memset(&intBuf, 0, sizeof(intBuf));
+
+ put4LE(intBuf, kSigVersion);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write signature version: %s\n", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, mVersion);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package version\n");
+ return false;
+ }
+
+ put4LE(intBuf, mFlags);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package version\n");
+ return false;
+ }
+
+ if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) {
+ LOGW("couldn't write salt: %s\n", strerror(errno));
+ return false;
+ }
+
+ size_t packageNameLen = mPackageName.size();
+ put4LE(intBuf, packageNameLen);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package name length: %s\n", strerror(errno));
+ return false;
+ }
+
+ if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
+ LOGW("couldn't write package name: %s\n", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, kPackageNameOffset + packageNameLen);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write footer size: %s\n", strerror(errno));
+ return false;
+ }
+
+ put4LE(intBuf, kSignature);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write footer magic signature: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool ObbFile::removeFrom(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_RDWR);
+ if (fd < 0) {
+ goto out;
+ }
+ success = removeFrom(fd);
+ close(fd);
+
+out:
+ if (!success) {
+ LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno));
+ }
+ return success;
+}
+
+bool ObbFile::removeFrom(int fd)
+{
+ if (fd < 0) {
+ return false;
+ }
+
+ if (!readFrom(fd)) {
+ return false;
+ }
+
+ ftruncate(fd, mFooterStart);
+
+ return true;
+}
+
+}
diff --git a/libs/utils/Pool.cpp b/libs/utils/Pool.cpp
new file mode 100644
index 0000000..8f18cb9
--- /dev/null
+++ b/libs/utils/Pool.cpp
@@ -0,0 +1,37 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// A simple memory pool.
+//
+#define LOG_TAG "Pool"
+
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <utils/Pool.h>
+
+#include <stdlib.h>
+
+namespace android {
+
+// TODO Provide a real implementation of a pool. This is just a stub for initial development.
+
+PoolImpl::PoolImpl(size_t objSize) :
+ mObjSize(objSize) {
+}
+
+PoolImpl::~PoolImpl() {
+}
+
+void* PoolImpl::allocImpl() {
+ void* ptr = malloc(mObjSize);
+ LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object.");
+ return ptr;
+}
+
+void PoolImpl::freeImpl(void* obj) {
+ LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object.");
+ return free(obj);
+}
+
+} // namespace android
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 319b8d9..f7bee02 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -1948,8 +1948,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass);
if (offset <= 0) {
if (offset < 0) {
- LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n",
- resID, t, e, (int)ip, (int)offset);
+ LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
+ resID, t, e, ip, (int)offset);
return offset;
}
continue;
@@ -4531,6 +4531,9 @@ void ResTable::print(bool inclValues) const
case ResTable_config::SCREENSIZE_LARGE:
printf(" (large)");
break;
+ case ResTable_config::SCREENSIZE_XLARGE:
+ printf(" (xlarge)");
+ break;
}
printf(" lng=%d",
type->config.screenLayout&ResTable_config::MASK_SCREENLONG);
diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp
index 68a1c52..b5dda2f 100644
--- a/libs/utils/StopWatch.cpp
+++ b/libs/utils/StopWatch.cpp
@@ -30,10 +30,9 @@ namespace android {
StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
- : mName(name), mClock(clock), mFlags(flags),
- mStartTime(0), mNumLaps(0)
+ : mName(name), mClock(clock), mFlags(flags)
{
- mStartTime = systemTime(mClock);
+ reset();
}
StopWatch::~StopWatch()
@@ -72,6 +71,12 @@ nsecs_t StopWatch::elapsedTime() const
return systemTime(mClock) - mStartTime;
}
+void StopWatch::reset()
+{
+ mNumLaps = 0;
+ mStartTime = systemTime(mClock);
+}
+
/*****************************************************************************/
diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp
new file mode 100644
index 0000000..1f62ac5
--- /dev/null
+++ b/libs/utils/StreamingZipInflater.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 1
+#define LOG_TAG "szipinf"
+#include <utils/Log.h>
+
+#include <utils/FileMap.h>
+#include <utils/StreamingZipInflater.h>
+#include <string.h>
+#include <stddef.h>
+#include <assert.h>
+
+static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; }
+
+using namespace android;
+
+/*
+ * Streaming access to compressed asset data in an open fd
+ */
+StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart,
+ size_t uncompSize, size_t compSize) {
+ mFd = fd;
+ mDataMap = NULL;
+ mInFileStart = compDataStart;
+ mOutTotalSize = uncompSize;
+ mInTotalSize = compSize;
+
+ mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE;
+ mInBuf = new uint8_t[mInBufSize];
+
+ mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
+ mOutBuf = new uint8_t[mOutBufSize];
+
+ initInflateState();
+}
+
+/*
+ * Streaming access to compressed data held in an mmapped region of memory
+ */
+StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
+ mFd = -1;
+ mDataMap = dataMap;
+ mOutTotalSize = uncompSize;
+ mInTotalSize = dataMap->getDataLength();
+
+ mInBuf = (uint8_t*) dataMap->getDataPtr();
+ mInBufSize = mInTotalSize;
+
+ mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
+ mOutBuf = new uint8_t[mOutBufSize];
+
+ initInflateState();
+}
+
+StreamingZipInflater::~StreamingZipInflater() {
+ // tear down the in-flight zip state just in case
+ ::inflateEnd(&mInflateState);
+
+ if (mDataMap == NULL) {
+ delete [] mInBuf;
+ }
+ delete [] mOutBuf;
+}
+
+void StreamingZipInflater::initInflateState() {
+ LOGD("Initializing inflate state");
+
+ memset(&mInflateState, 0, sizeof(mInflateState));
+ mInflateState.zalloc = Z_NULL;
+ mInflateState.zfree = Z_NULL;
+ mInflateState.opaque = Z_NULL;
+ mInflateState.next_in = (Bytef*)mInBuf;
+ mInflateState.next_out = (Bytef*) mOutBuf;
+ mInflateState.avail_out = mOutBufSize;
+ mInflateState.data_type = Z_UNKNOWN;
+
+ mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0;
+ mInNextChunkOffset = 0;
+ mStreamNeedsInit = true;
+
+ if (mDataMap == NULL) {
+ ::lseek(mFd, mInFileStart, SEEK_SET);
+ mInflateState.avail_in = 0; // set when a chunk is read in
+ } else {
+ mInflateState.avail_in = mInBufSize;
+ }
+}
+
+/*
+ * Basic approach:
+ *
+ * 1. If we have undelivered uncompressed data, send it. At this point
+ * either we've satisfied the request, or we've exhausted the available
+ * output data in mOutBuf.
+ *
+ * 2. While we haven't sent enough data to satisfy the request:
+ * 0. if the request is for more data than exists, bail.
+ * a. if there is no input data to decode, read some into the input buffer
+ * and readjust the z_stream input pointers
+ * b. point the output to the start of the output buffer and decode what we can
+ * c. deliver whatever output data we can
+ */
+ssize_t StreamingZipInflater::read(void* outBuf, size_t count) {
+ uint8_t* dest = (uint8_t*) outBuf;
+ size_t bytesRead = 0;
+ size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition));
+ while (toRead > 0) {
+ // First, write from whatever we already have decoded and ready to go
+ size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable);
+ if (deliverable > 0) {
+ if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable);
+ mOutDeliverable += deliverable;
+ mOutCurPosition += deliverable;
+ dest += deliverable;
+ bytesRead += deliverable;
+ toRead -= deliverable;
+ }
+
+ // need more data? time to decode some.
+ if (toRead > 0) {
+ // if we don't have any data to decode, read some in. If we're working
+ // from mmapped data this won't happen, because the clipping to total size
+ // will prevent reading off the end of the mapped input chunk.
+ if (mInflateState.avail_in == 0) {
+ int err = readNextChunk();
+ if (err < 0) {
+ LOGE("Unable to access asset data: %d", err);
+ if (!mStreamNeedsInit) {
+ ::inflateEnd(&mInflateState);
+ initInflateState();
+ }
+ return -1;
+ }
+ }
+ // we know we've drained whatever is in the out buffer now, so just
+ // start from scratch there, reading all the input we have at present.
+ mInflateState.next_out = (Bytef*) mOutBuf;
+ mInflateState.avail_out = mOutBufSize;
+
+ /*
+ LOGD("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p",
+ mInflateState.avail_in, mInflateState.avail_out,
+ mInflateState.next_in, mInflateState.next_out);
+ */
+ int result = Z_OK;
+ if (mStreamNeedsInit) {
+ LOGD("Initializing zlib to inflate");
+ result = inflateInit2(&mInflateState, -MAX_WBITS);
+ mStreamNeedsInit = false;
+ }
+ if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH);
+ if (result < 0) {
+ // Whoops, inflation failed
+ LOGE("Error inflating asset: %d", result);
+ ::inflateEnd(&mInflateState);
+ initInflateState();
+ return -1;
+ } else {
+ if (result == Z_STREAM_END) {
+ // we know we have to have reached the target size here and will
+ // not try to read any further, so just wind things up.
+ ::inflateEnd(&mInflateState);
+ }
+
+ // Note how much data we got, and off we go
+ mOutDeliverable = 0;
+ mOutLastDecoded = mOutBufSize - mInflateState.avail_out;
+ }
+ }
+ }
+ return bytesRead;
+}
+
+int StreamingZipInflater::readNextChunk() {
+ assert(mDataMap == NULL);
+
+ if (mInNextChunkOffset < mInTotalSize) {
+ size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset);
+ if (toRead > 0) {
+ ssize_t didRead = ::read(mFd, mInBuf, toRead);
+ //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead);
+ if (didRead < 0) {
+ // TODO: error
+ LOGE("Error reading asset data");
+ return didRead;
+ } else {
+ mInNextChunkOffset += didRead;
+ mInflateState.next_in = (Bytef*) mInBuf;
+ mInflateState.avail_in = didRead;
+ }
+ }
+ }
+ return 0;
+}
+
+// seeking backwards requires uncompressing fom the beginning, so is very
+// expensive. seeking forwards only requires uncompressing from the current
+// position to the destination.
+off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) {
+ if (absoluteInputPosition < mOutCurPosition) {
+ // rewind and reprocess the data from the beginning
+ if (!mStreamNeedsInit) {
+ ::inflateEnd(&mInflateState);
+ }
+ initInflateState();
+ read(NULL, absoluteInputPosition);
+ } else if (absoluteInputPosition > mOutCurPosition) {
+ read(NULL, absoluteInputPosition - mOutCurPosition);
+ }
+ // else if the target position *is* our current position, do nothing
+ return absoluteInputPosition;
+}
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 636cd83..1c4f80c 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -301,8 +301,9 @@ void String8::setTo(const String8& other)
status_t String8::setTo(const char* other)
{
+ const char *newString = allocFromUTF8(other, strlen(other));
SharedBuffer::bufferFromData(mString)->release();
- mString = allocFromUTF8(other, strlen(other));
+ mString = newString;
if (mString) return NO_ERROR;
mString = getEmptyString();
@@ -311,8 +312,9 @@ status_t String8::setTo(const char* other)
status_t String8::setTo(const char* other, size_t len)
{
+ const char *newString = allocFromUTF8(other, len);
SharedBuffer::bufferFromData(mString)->release();
- mString = allocFromUTF8(other, len);
+ mString = newString;
if (mString) return NO_ERROR;
mString = getEmptyString();
@@ -321,8 +323,9 @@ status_t String8::setTo(const char* other, size_t len)
status_t String8::setTo(const char16_t* other, size_t len)
{
+ const char *newString = allocFromUTF16(other, len);
SharedBuffer::bufferFromData(mString)->release();
- mString = allocFromUTF16(other, len);
+ mString = newString;
if (mString) return NO_ERROR;
mString = getEmptyString();
@@ -331,8 +334,9 @@ status_t String8::setTo(const char16_t* other, size_t len)
status_t String8::setTo(const char32_t* other, size_t len)
{
+ const char *newString = allocFromUTF32(other, len);
SharedBuffer::bufferFromData(mString)->release();
- mString = allocFromUTF32(other, len);
+ mString = newString;
if (mString) return NO_ERROR;
mString = getEmptyString();
@@ -368,6 +372,27 @@ status_t String8::append(const char* other, size_t otherLen)
return real_append(other, otherLen);
}
+status_t String8::appendFormat(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ int result = NO_ERROR;
+ int n = vsnprintf(NULL, 0, fmt, ap);
+ if (n != 0) {
+ size_t oldLength = length();
+ char* buf = lockBuffer(oldLength + n);
+ if (buf) {
+ vsnprintf(buf + oldLength, n + 1, fmt, ap);
+ } else {
+ result = NO_MEMORY;
+ }
+ }
+
+ va_end(ap);
+ return result;
+}
+
status_t String8::real_append(const char* other, size_t otherLen)
{
const size_t myLen = bytes();
@@ -407,15 +432,16 @@ status_t String8::unlockBuffer(size_t size)
if (size != this->size()) {
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize(size+1);
- if (buf) {
- char* str = (char*)buf->data();
- str[size] = 0;
- mString = str;
- return NO_ERROR;
+ if (! buf) {
+ return NO_MEMORY;
}
+
+ char* str = (char*)buf->data();
+ str[size] = 0;
+ mString = str;
}
-
- return NO_MEMORY;
+
+ return NO_ERROR;
}
ssize_t String8::find(const char* other, size_t start) const
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 2b1f490..f6c55e4 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -21,6 +21,7 @@
#include <utils/Log.h>
#include <cutils/sched_policy.h>
+#include <cutils/properties.h>
#include <stdio.h>
#include <stdlib.h>
@@ -57,13 +58,27 @@ using namespace android;
// ----------------------------------------------------------------------------
/*
- * Create and run a new thead.
+ * Create and run a new thread.
*
* We create it "detached", so it cleans up after itself.
*/
typedef void* (*android_pthread_entry)(void*);
+static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT;
+static bool gDoSchedulingGroup = true;
+
+static void checkDoSchedulingGroup(void) {
+ char buf[PROPERTY_VALUE_MAX];
+ int len = property_get("debug.sys.noschedgroups", buf, "");
+ if (len > 0) {
+ int temp;
+ if (sscanf(buf, "%d", &temp) == 1) {
+ gDoSchedulingGroup = temp == 0;
+ }
+ }
+}
+
struct thread_data_t {
thread_func_t entryFunction;
void* userData;
@@ -79,6 +94,15 @@ struct thread_data_t {
char * name = t->threadName;
delete t;
setpriority(PRIO_PROCESS, 0, prio);
+ pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+ if (gDoSchedulingGroup) {
+ if (prio >= ANDROID_PRIORITY_BACKGROUND) {
+ set_sched_policy(androidGetTid(), SP_BACKGROUND);
+ } else {
+ set_sched_policy(androidGetTid(), SP_FOREGROUND);
+ }
+ }
+
if (name) {
#if defined(HAVE_PRCTL)
// Mac OS doesn't have this, and we build libutil for the host too
@@ -287,9 +311,12 @@ int androidSetThreadSchedulingGroup(pid_t tid, int grp)
}
#if defined(HAVE_PTHREADS)
- if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
- SP_BACKGROUND : SP_FOREGROUND)) {
- return PERMISSION_DENIED;
+ pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+ if (gDoSchedulingGroup) {
+ if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+ SP_BACKGROUND : SP_FOREGROUND)) {
+ return PERMISSION_DENIED;
+ }
}
#endif
@@ -303,10 +330,13 @@ int androidSetThreadPriority(pid_t tid, int pri)
#if defined(HAVE_PTHREADS)
int lasterr = 0;
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = set_sched_policy(tid, SP_BACKGROUND);
- } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
- rc = set_sched_policy(tid, SP_FOREGROUND);
+ pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
+ if (gDoSchedulingGroup) {
+ if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_BACKGROUND);
+ } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+ rc = set_sched_policy(tid, SP_FOREGROUND);
+ }
}
if (rc) {
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index 0322af7..289c826 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -108,18 +108,28 @@ size_t VectorImpl::capacity() const
ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
{
+ return insertArrayAt(vector.arrayImpl(), index, vector.size());
+}
+
+ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+{
+ return insertVectorAt(vector, size());
+}
+
+ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)
+{
if (index > size())
return BAD_INDEX;
- void* where = _grow(index, vector.size());
+ void* where = _grow(index, length);
if (where) {
- _do_copy(where, vector.arrayImpl(), vector.size());
+ _do_copy(where, array, length);
}
return where ? index : (ssize_t)NO_MEMORY;
}
-ssize_t VectorImpl::appendVector(const VectorImpl& vector)
+ssize_t VectorImpl::appendArray(const void* array, size_t length)
{
- return insertVectorAt(vector, size());
+ return insertArrayAt(array, size(), length);
}
ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp
index 45f6c8b..16b219c 100644
--- a/libs/utils/ZipFileCRO.cpp
+++ b/libs/utils/ZipFileCRO.cpp
@@ -39,8 +39,8 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken,
}
bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken,
- int* pMethod, long* pUncompLen,
- long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) {
+ int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) {
ZipFileRO* zip = (ZipFileRO*)zipToken;
ZipEntryRO entry = (ZipEntryRO)entryToken;
return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset,
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 6c701dd..4261196 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -22,6 +22,7 @@
#include <utils/ZipFileRO.h>
#include <utils/Log.h>
#include <utils/misc.h>
+#include <utils/threads.h>
#include <zlib.h>
@@ -29,6 +30,38 @@
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
+#include <unistd.h>
+
+#if HAVE_PRINTF_ZD
+# define ZD "%zd"
+# define ZD_TYPE ssize_t
+#else
+# define ZD "%ld"
+# define ZD_TYPE long
+#endif
+
+/*
+ * We must open binary files using open(path, ... | O_BINARY) under Windows.
+ * Otherwise strange read errors will happen.
+ */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
using namespace android;
@@ -38,6 +71,7 @@ using namespace android;
#define kEOCDSignature 0x06054b50
#define kEOCDLen 22
#define kEOCDNumEntries 8 // offset to #of entries in file
+#define kEOCDSize 12 // size of the central directory
#define kEOCDFileOffset 16 // offset to central directory
#define kMaxCommentLen 65535 // longest possible in ushort
@@ -68,6 +102,16 @@ using namespace android;
*/
#define kZipEntryAdj 10000
+ZipFileRO::~ZipFileRO() {
+ free(mHashTable);
+ if (mDirectoryMap)
+ mDirectoryMap->release();
+ if (mFd >= 0)
+ TEMP_FAILURE_RETRY(close(mFd));
+ if (mFileName)
+ free(mFileName);
+}
+
/*
* Convert a ZipEntryRO to a hash table index, verifying that it's in a
* valid range.
@@ -90,185 +134,251 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
status_t ZipFileRO::open(const char* zipFileName)
{
int fd = -1;
- off_t length;
- assert(mFileMap == NULL);
+ assert(mDirectoryMap == NULL);
/*
* Open and map the specified file.
*/
- fd = ::open(zipFileName, O_RDONLY);
+ fd = ::open(zipFileName, O_RDONLY | O_BINARY);
if (fd < 0) {
LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
return NAME_NOT_FOUND;
}
- length = lseek(fd, 0, SEEK_END);
- if (length < 0) {
- close(fd);
+ mFileLength = lseek(fd, 0, SEEK_END);
+ if (mFileLength < kEOCDLen) {
+ TEMP_FAILURE_RETRY(close(fd));
return UNKNOWN_ERROR;
}
- mFileMap = new FileMap();
- if (mFileMap == NULL) {
- close(fd);
- return NO_MEMORY;
- }
- if (!mFileMap->create(zipFileName, fd, 0, length, true)) {
- LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno));
- close(fd);
- return UNKNOWN_ERROR;
+ if (mFileName != NULL) {
+ free(mFileName);
}
+ mFileName = strdup(zipFileName);
mFd = fd;
/*
- * Got it mapped, verify it and create data structures for fast access.
+ * Find the Central Directory and store its size and number of entries.
+ */
+ if (!mapCentralDirectory()) {
+ goto bail;
+ }
+
+ /*
+ * Verify Central Directory and create data structures for fast access.
*/
if (!parseZipArchive()) {
- mFileMap->release();
- mFileMap = NULL;
- return UNKNOWN_ERROR;
+ goto bail;
}
return OK;
+
+bail:
+ free(mFileName);
+ mFileName = NULL;
+ TEMP_FAILURE_RETRY(close(fd));
+ return UNKNOWN_ERROR;
}
/*
* Parse the Zip archive, verifying its contents and initializing internal
* data structures.
*/
-bool ZipFileRO::parseZipArchive(void)
+bool ZipFileRO::mapCentralDirectory(void)
{
-#define CHECK_OFFSET(_off) { \
- if ((unsigned int) (_off) >= maxOffset) { \
- LOGE("ERROR: bad offset %u (max %d): %s\n", \
- (unsigned int) (_off), maxOffset, #_off); \
- goto bail; \
- } \
- }
- const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr();
- const unsigned char* ptr;
- size_t length = mFileMap->getDataLength();
- bool result = false;
- unsigned int i, numEntries, cdOffset;
- unsigned int val;
+ ssize_t readAmount = kMaxEOCDSearch;
+ if (readAmount > (ssize_t) mFileLength)
+ readAmount = mFileLength;
+
+ unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
+ if (scanBuf == NULL) {
+ LOGW("couldn't allocate scanBuf: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
/*
- * The first 4 bytes of the file will either be the local header
- * signature for the first file (kLFHSignature) or, if the archive doesn't
- * have any files in it, the end-of-central-directory signature
- * (kEOCDSignature).
+ * Make sure this is a Zip archive.
*/
- val = get4LE(basePtr);
- if (val == kEOCDSignature) {
- LOGI("Found Zip archive, but it looks empty\n");
- goto bail;
- } else if (val != kLFHSignature) {
- LOGV("Not a Zip archive (found 0x%08x)\n", val);
- goto bail;
+ if (lseek(mFd, 0, SEEK_SET) != 0) {
+ LOGW("seek to start failed: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t)));
+ if (actual != (ssize_t) sizeof(int32_t)) {
+ LOGI("couldn't read first signature from zip archive: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ {
+ unsigned int header = get4LE(scanBuf);
+ if (header == kEOCDSignature) {
+ LOGI("Found Zip archive, but it looks empty\n");
+ free(scanBuf);
+ return false;
+ } else if (header != kLFHSignature) {
+ LOGV("Not a Zip archive (found 0x%08x)\n", header);
+ free(scanBuf);
+ return false;
+ }
}
/*
- * Find the EOCD. We'll find it immediately unless they have a file
- * comment.
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
*/
- ptr = basePtr + length - kEOCDLen;
+ off_t searchStart = mFileLength - readAmount;
- while (ptr >= basePtr) {
- if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature)
+ if (lseek(mFd, searchStart, SEEK_SET) != searchStart) {
+ LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+ actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
+ if (actual != (ssize_t) readAmount) {
+ LOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
+ (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ int i;
+ for (i = readAmount - kEOCDLen; i >= 0; i--) {
+ if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
+ LOGV("+++ Found EOCD at buf+%d\n", i);
break;
- ptr--;
+ }
}
- if (ptr < basePtr) {
- LOGI("Could not find end-of-central-directory in Zip\n");
- goto bail;
+ if (i < 0) {
+ LOGD("Zip: EOCD not found, %s is not zip\n", mFileName);
+ free(scanBuf);
+ return false;
}
+ off_t eocdOffset = searchStart + i;
+ const unsigned char* eocdPtr = scanBuf + i;
+
+ assert(eocdOffset < mFileLength);
+
/*
- * There are two interesting items in the EOCD block: the number of
- * entries in the file, and the file offset of the start of the
- * central directory.
- *
- * (There's actually a count of the #of entries in this file, and for
- * all files which comprise a spanned archive, but for our purposes
- * we're only interested in the current file. Besides, we expect the
- * two to be equivalent for our stuff.)
+ * Grab the CD offset and size, and the number of entries in the
+ * archive. After that, we can release our EOCD hunt buffer.
*/
- numEntries = get2LE(ptr + kEOCDNumEntries);
- cdOffset = get4LE(ptr + kEOCDFileOffset);
+ unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries);
+ unsigned int dirSize = get4LE(eocdPtr + kEOCDSize);
+ unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+ free(scanBuf);
+
+ // Verify that they look reasonable.
+ if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
+ LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
+ (long) dirOffset, dirSize, (long) eocdOffset);
+ return false;
+ }
+ if (numEntries == 0) {
+ LOGW("empty archive?\n");
+ return false;
+ }
- /* valid offsets are [0,EOCD] */
- unsigned int maxOffset;
- maxOffset = (ptr - basePtr) +1;
+ LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+ numEntries, dirSize, dirOffset);
- LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
- if (numEntries == 0 || cdOffset >= length) {
- LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
- numEntries, cdOffset, length);
- goto bail;
+ mDirectoryMap = new FileMap();
+ if (mDirectoryMap == NULL) {
+ LOGW("Unable to create directory map: %s", strerror(errno));
+ return false;
+ }
+
+ if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) {
+ LOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
+ (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno));
+ return false;
}
+ mNumEntries = numEntries;
+ mDirectoryOffset = dirOffset;
+
+ return true;
+}
+
+bool ZipFileRO::parseZipArchive(void)
+{
+ bool result = false;
+ const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr();
+ size_t cdLength = mDirectoryMap->getDataLength();
+ int numEntries = mNumEntries;
+
/*
* Create hash table. We have a minimum 75% load factor, possibly as
* low as 50% after we round off to a power of 2.
*/
- mNumEntries = numEntries;
- mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3));
- mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize);
+ mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3);
+ mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry));
/*
* Walk through the central directory, adding entries to the hash
* table.
*/
- ptr = basePtr + cdOffset;
- for (i = 0; i < numEntries; i++) {
- unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
- const unsigned char* localHdr;
- unsigned int hash;
-
+ const unsigned char* ptr = cdPtr;
+ for (int i = 0; i < numEntries; i++) {
if (get4LE(ptr) != kCDESignature) {
LOGW("Missed a central dir sig (at %d)\n", i);
goto bail;
}
- if (ptr + kCDELen > basePtr + length) {
+ if (ptr + kCDELen > cdPtr + cdLength) {
LOGW("Ran off the end (at %d)\n", i);
goto bail;
}
- localHdrOffset = get4LE(ptr + kCDELocalOffset);
- CHECK_OFFSET(localHdrOffset);
+ long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset >= mDirectoryOffset) {
+ LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i);
+ goto bail;
+ }
+
+ unsigned int fileNameLen, extraLen, commentLen, hash;
+
fileNameLen = get2LE(ptr + kCDENameLen);
extraLen = get2LE(ptr + kCDEExtraLen);
commentLen = get2LE(ptr + kCDECommentLen);
- //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n",
- // i, localHdrOffset, fileNameLen, extraLen, commentLen);
- //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen);
-
/* add the CDE filename to the hash table */
hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
addToHash((const char*)ptr + kCDELen, fileNameLen, hash);
- localHdr = basePtr + localHdrOffset;
- if (get4LE(localHdr) != kLFHSignature) {
- LOGW("Bad offset to local header: %d (at %d)\n",
- localHdrOffset, i);
+ ptr += kCDELen + fileNameLen + extraLen + commentLen;
+ if ((size_t)(ptr - cdPtr) > cdLength) {
+ LOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
+ (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
goto bail;
}
-
- ptr += kCDELen + fileNameLen + extraLen + commentLen;
- CHECK_OFFSET(ptr - basePtr);
}
-
+ LOGV("+++ zip good scan %d entries\n", numEntries);
result = true;
bail:
return result;
-#undef CHECK_OFFSET
}
-
/*
* Simple string hash function for non-null-terminated strings.
*/
@@ -302,10 +412,18 @@ void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash)
/*
* Find a matching entry.
*
- * Returns 0 if not found.
+ * Returns NULL if not found.
*/
ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
{
+ /*
+ * If the ZipFileRO instance is not initialized, the entry number will
+ * end up being garbage since mHashTableSize is -1.
+ */
+ if (mHashTableSize <= 0) {
+ return NULL;
+ }
+
int nameLen = strlen(fileName);
unsigned int hash = computeHash(fileName, nameLen);
int ent = hash & (mHashTableSize-1);
@@ -315,7 +433,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
memcmp(mHashTable[ent].name, fileName, nameLen) == 0)
{
/* match */
- return (ZipEntryRO) (ent + kZipEntryAdj);
+ return (ZipEntryRO)(long)(ent + kZipEntryAdj);
}
ent = (ent + 1) & (mHashTableSize-1);
@@ -354,20 +472,24 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const
* Returns "false" if the offsets to the fields or the contents of the fields
* appear to be bogus.
*/
-bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen,
- long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const
+bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const
{
- int ent = entryToIndex(entry);
+ bool ret = false;
+
+ const int ent = entryToIndex(entry);
if (ent < 0)
return false;
+ HashEntry hashEntry = mHashTable[ent];
+
/*
* Recover the start of the central directory entry from the filename
- * pointer.
+ * pointer. The filename is the first entry past the fixed-size data,
+ * so we can just subtract back from that.
*/
- const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr();
- const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name;
- size_t zipLength = mFileMap->getDataLength();
+ const unsigned char* ptr = (const unsigned char*) hashEntry.name;
+ off_t cdOffset = mDirectoryOffset;
ptr -= kCDELen;
@@ -380,48 +502,117 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen,
if (pCrc32 != NULL)
*pCrc32 = get4LE(ptr + kCDECRC);
+ size_t compLen = get4LE(ptr + kCDECompLen);
+ if (pCompLen != NULL)
+ *pCompLen = compLen;
+ size_t uncompLen = get4LE(ptr + kCDEUncompLen);
+ if (pUncompLen != NULL)
+ *pUncompLen = uncompLen;
+
/*
- * We need to make sure that the lengths are not so large that somebody
- * trying to map the compressed or uncompressed data runs off the end
- * of the mapped region.
+ * If requested, determine the offset of the start of the data. All we
+ * have is the offset to the Local File Header, which is variable size,
+ * so we have to read the contents of the struct to figure out where
+ * the actual data starts.
+ *
+ * We also need to make sure that the lengths are not so large that
+ * somebody trying to map the compressed or uncompressed data runs
+ * off the end of the mapped region.
+ *
+ * Note we don't verify compLen/uncompLen if they don't request the
+ * dataOffset, because dataOffset is expensive to determine. However,
+ * if they don't have the file offset, they're not likely to be doing
+ * anything with the contents.
*/
- unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset);
- if (localHdrOffset + kLFHLen >= zipLength) {
- LOGE("ERROR: bad local hdr offset in zip\n");
- return false;
- }
- const unsigned char* localHdr = basePtr + localHdrOffset;
- off_t dataOffset = localHdrOffset + kLFHLen
- + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen);
- if ((unsigned long) dataOffset >= zipLength) {
- LOGE("ERROR: bad data offset in zip\n");
- return false;
- }
+ if (pOffset != NULL) {
+ long localHdrOffset = get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset + kLFHLen >= cdOffset) {
+ LOGE("ERROR: bad local hdr offset in zip\n");
+ return false;
+ }
+
+ unsigned char lfhBuf[kLFHLen];
- if (pCompLen != NULL) {
- *pCompLen = get4LE(ptr + kCDECompLen);
- if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) {
- LOGE("ERROR: bad compressed length in zip\n");
+#ifdef HAVE_PREAD
+ /*
+ * This file descriptor might be from zygote's preloaded assets,
+ * so we need to do an pread() instead of a lseek() + read() to
+ * guarantee atomicity across the processes with the shared file
+ * descriptors.
+ */
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
+
+ if (actual != sizeof(lfhBuf)) {
+ LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
return false;
}
- }
- if (pUncompLen != NULL) {
- *pUncompLen = get4LE(ptr + kCDEUncompLen);
- if (*pUncompLen < 0) {
- LOGE("ERROR: negative uncompressed length in zip\n");
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, get4LE(lfhBuf));
+ return false;
+ }
+#else /* HAVE_PREAD */
+ /*
+ * For hosts don't have pread() we cannot guarantee atomic reads from
+ * an offset in a file. Android should never run on those platforms.
+ * File descriptors inherited from a fork() share file offsets and
+ * there would be nothing to protect from two different processes
+ * calling lseek() concurrently.
+ */
+
+ {
+ AutoMutex _l(mFdLock);
+
+ if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+ if (actual != sizeof(lfhBuf)) {
+ LOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ off_t actualOffset = lseek(mFd, 0, SEEK_CUR);
+ LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: offset=" ZD " data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
+ return false;
+ }
+ }
+#endif /* HAVE_PREAD */
+
+ off_t dataOffset = localHdrOffset + kLFHLen
+ + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+ if (dataOffset >= cdOffset) {
+ LOGW("bad data offset %ld in zip\n", (long) dataOffset);
return false;
}
+
+ /* check lengths */
+ if ((off_t)(dataOffset + compLen) > cdOffset) {
+ LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
+ return false;
+ }
+
if (method == kCompressStored &&
- (size_t)(dataOffset + *pUncompLen) >= zipLength)
+ (off_t)(dataOffset + uncompLen) > cdOffset)
{
- LOGE("ERROR: bad uncompressed length in zip\n");
+ LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
return false;
}
- }
- if (pOffset != NULL) {
*pOffset = dataOffset;
}
+
return true;
}
@@ -457,14 +648,14 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
*/
FileMap* newMap;
- long compLen;
+ size_t compLen;
off_t offset;
if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL))
return NULL;
newMap = new FileMap();
- if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) {
+ if (!newMap->create(mFileName, mFd, offset, compLen, true)) {
newMap->release();
return NULL;
}
@@ -480,19 +671,26 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
*/
bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
{
- const int kSequentialMin = 32768;
+ const size_t kSequentialMin = 32768;
bool result = false;
int ent = entryToIndex(entry);
if (ent < 0)
return -1;
- const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr();
int method;
- long uncompLen, compLen;
+ size_t uncompLen, compLen;
off_t offset;
+ const unsigned char* ptr;
getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
+
/*
* Experiment with madvise hint. When we want to uncompress a file,
* we pull some stuff out of the central dir entry and then hit a
@@ -507,20 +705,22 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
* pair of system calls are negated by a reduction in page faults.
*/
if (compLen > kSequentialMin)
- mFileMap->advise(FileMap::SEQUENTIAL);
+ file->advise(FileMap::SEQUENTIAL);
if (method == kCompressStored) {
- memcpy(buffer, basePtr + offset, uncompLen);
+ memcpy(buffer, ptr, uncompLen);
} else {
- if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen))
- goto bail;
+ if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
+ goto unmap;
}
if (compLen > kSequentialMin)
- mFileMap->advise(FileMap::NORMAL);
+ file->advise(FileMap::NORMAL);
result = true;
+unmap:
+ file->release();
bail:
return result;
}
@@ -537,34 +737,41 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
if (ent < 0)
return -1;
- const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr();
int method;
- long uncompLen, compLen;
+ size_t uncompLen, compLen;
off_t offset;
+ const unsigned char* ptr;
getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
- if (method == kCompressStored) {
- ssize_t actual;
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
- actual = write(fd, basePtr + offset, uncompLen);
+ ptr = (const unsigned char*) file->getDataPtr();
+
+ if (method == kCompressStored) {
+ ssize_t actual = write(fd, ptr, uncompLen);
if (actual < 0) {
LOGE("Write failed: %s\n", strerror(errno));
- goto bail;
- } else if (actual != uncompLen) {
- LOGE("Partial write during uncompress (%d of %ld)\n",
- (int)actual, uncompLen);
- goto bail;
+ goto unmap;
+ } else if ((size_t) actual != uncompLen) {
+ LOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
+ (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
+ goto unmap;
} else {
LOGI("+++ successful write\n");
}
} else {
- if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen))
- goto bail;
+ if (!inflateBuffer(fd, ptr, uncompLen, compLen))
+ goto unmap;
}
result = true;
+unmap:
+ file->release();
bail:
return result;
}
@@ -573,7 +780,7 @@ bail:
* Uncompress "deflate" data from one buffer to another.
*/
/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf,
- long uncompLen, long compLen)
+ size_t uncompLen, size_t compLen)
{
bool result = false;
z_stream zstream;
@@ -582,7 +789,7 @@ bail:
/*
* Initialize the zlib stream struct.
*/
- memset(&zstream, 0, sizeof(zstream));
+ memset(&zstream, 0, sizeof(zstream));
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
@@ -592,10 +799,10 @@ bail:
zstream.avail_out = uncompLen;
zstream.data_type = Z_UNKNOWN;
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
zerr = inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
@@ -619,9 +826,9 @@ bail:
}
/* paranoia */
- if ((long) zstream.total_out != uncompLen) {
- LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
- zstream.total_out, uncompLen);
+ if (zstream.total_out != uncompLen) {
+ LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
goto z_bail;
}
@@ -638,10 +845,10 @@ bail:
* Uncompress "deflate" data from one buffer to an open file descriptor.
*/
/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf,
- long uncompLen, long compLen)
+ size_t uncompLen, size_t compLen)
{
bool result = false;
- const int kWriteBufSize = 32768;
+ const size_t kWriteBufSize = 32768;
unsigned char writeBuf[kWriteBufSize];
z_stream zstream;
int zerr;
@@ -649,7 +856,7 @@ bail:
/*
* Initialize the zlib stream struct.
*/
- memset(&zstream, 0, sizeof(zstream));
+ memset(&zstream, 0, sizeof(zstream));
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
@@ -659,10 +866,10 @@ bail:
zstream.avail_out = sizeof(writeBuf);
zstream.data_type = Z_UNKNOWN;
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
zerr = inflateInit2(&zstream, -MAX_WBITS);
if (zerr != Z_OK) {
if (zerr == Z_VERSION_ERROR) {
@@ -708,9 +915,9 @@ bail:
assert(zerr == Z_STREAM_END); /* other errors should've been caught */
/* paranoia */
- if ((long) zstream.total_out != uncompLen) {
- LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
- zstream.total_out, uncompLen);
+ if (zstream.total_out != uncompLen) {
+ LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
goto z_bail;
}
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
new file mode 100644
index 0000000..00077ee
--- /dev/null
+++ b/libs/utils/tests/Android.mk
@@ -0,0 +1,45 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# Build the unit tests.
+test_src_files := \
+ ObbFile_test.cpp \
+ Looper_test.cpp \
+ String8_test.cpp
+
+shared_libraries := \
+ libz \
+ liblog \
+ libcutils \
+ libutils \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+c_includes := \
+ external/zlib \
+ external/icu4c/common \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+ $(eval include $(BUILD_EXECUTABLE)) \
+)
+
+endif
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
new file mode 100644
index 0000000..cea1313
--- /dev/null
+++ b/libs/utils/tests/Looper_test.cpp
@@ -0,0 +1,425 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+#include <utils/StopWatch.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "TestHelpers.h"
+
+// # of milliseconds to fudge stopwatch measurements
+#define TIMING_TOLERANCE_MS 25
+
+namespace android {
+
+class DelayedWake : public DelayedTask {
+ sp<Looper> mLooper;
+
+public:
+ DelayedWake(int delayMillis, const sp<Looper> looper) :
+ DelayedTask(delayMillis), mLooper(looper) {
+ }
+
+protected:
+ virtual void doTask() {
+ mLooper->wake();
+ }
+};
+
+class DelayedWriteSignal : public DelayedTask {
+ Pipe* mPipe;
+
+public:
+ DelayedWriteSignal(int delayMillis, Pipe* pipe) :
+ DelayedTask(delayMillis), mPipe(pipe) {
+ }
+
+protected:
+ virtual void doTask() {
+ mPipe->writeSignal();
+ }
+};
+
+class CallbackHandler {
+public:
+ void setCallback(const sp<Looper>& looper, int fd, int events) {
+ looper->addFd(fd, 0, events, staticHandler, this);
+ }
+
+protected:
+ virtual ~CallbackHandler() { }
+
+ virtual int handler(int fd, int events) = 0;
+
+private:
+ static int staticHandler(int fd, int events, void* data) {
+ return static_cast<CallbackHandler*>(data)->handler(fd, events);
+ }
+};
+
+class StubCallbackHandler : public CallbackHandler {
+public:
+ int nextResult;
+ int callbackCount;
+
+ int fd;
+ int events;
+
+ StubCallbackHandler(int nextResult) : nextResult(nextResult),
+ callbackCount(0), fd(-1), events(-1) {
+ }
+
+protected:
+ virtual int handler(int fd, int events) {
+ callbackCount += 1;
+ this->fd = fd;
+ this->events = events;
+ return nextResult;
+ }
+};
+
+class LooperTest : public testing::Test {
+protected:
+ sp<Looper> mLooper;
+
+ virtual void SetUp() {
+ mLooper = new Looper(true);
+ }
+
+ virtual void TearDown() {
+ mLooper.clear();
+ }
+};
+
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {
+ mLooper->wake();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because wake() was called before waiting";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {
+ sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);
+ delayedWake->run();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal wake delay";
+ EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ ASSERT_EQ(OK, pipe.writeSignal());
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not have been invoked because FD was not signalled";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ pipe.writeSignal();
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+ sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+ delayedWriteSignal->run();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(1000);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal signal delay";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked exactly once";
+ EXPECT_EQ(pipe.receiveFd, handler.fd)
+ << "callback should have received pipe fd as parameter";
+ EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events)
+ << "callback should have received ALOOPER_EVENT_INPUT as events";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler(true);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+ pipe.writeSignal(); // would cause FD to be considered signalled
+ mLooper->removeFd(pipe.receiveFd);
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal timeout because FD was no longer registered";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+ EXPECT_EQ(0, handler.callbackCount)
+ << "callback should not be invoked";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ // First loop: Callback is registered and FD is signalled.
+ pipe.writeSignal();
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(0);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal zero because FD was already signalled";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should be invoked";
+
+ // Second loop: Callback is no longer registered and FD is signalled.
+ pipe.writeSignal();
+
+ stopWatch.reset();
+ result = mLooper->pollOnce(0);
+ elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. equal zero because timeout was zero";
+ EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+ << "pollOnce result should be ALOOPER_POLL_TIMEOUT";
+ EXPECT_EQ(1, handler.callbackCount)
+ << "callback should not be invoked this time";
+}
+
+TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {
+ const int expectedIdent = 5;
+ void* expectedData = this;
+
+ Pipe pipe;
+
+ pipe.writeSignal();
+ mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData);
+
+ StopWatch stopWatch("pollOnce");
+ int fd;
+ int events;
+ void* data;
+ int result = mLooper->pollOnce(100, &fd, &events, &data);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should be approx. zero";
+ EXPECT_EQ(expectedIdent, result)
+ << "pollOnce result should be the ident of the FD that was signalled";
+ EXPECT_EQ(pipe.receiveFd, fd)
+ << "pollOnce should have returned the received pipe fd";
+ EXPECT_EQ(ALOOPER_EVENT_INPUT, events)
+ << "pollOnce should have returned ALOOPER_EVENT_INPUT as events";
+ EXPECT_EQ(expectedData, data)
+ << "pollOnce should have returned the data";
+}
+
+TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {
+ Pipe pipe;
+ int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL);
+
+ EXPECT_EQ(1, result)
+ << "addFd should return 1 because FD was added";
+}
+
+TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {
+ Pipe pipe;
+ int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL);
+
+ EXPECT_EQ(-1, result)
+ << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {
+ Pipe pipe;
+ sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);
+ int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL);
+
+ EXPECT_EQ(-1, result)
+ << "addFd should return -1 because arguments were invalid";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {
+ int result = mLooper->removeFd(1);
+
+ EXPECT_EQ(0, result)
+ << "removeFd should return 0 because FD not registered";
+}
+
+TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {
+ Pipe pipe;
+ StubCallbackHandler handler(false);
+ handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+
+ // First time.
+ int result = mLooper->removeFd(pipe.receiveFd);
+
+ EXPECT_EQ(1, result)
+ << "removeFd should return 1 first time because FD was registered";
+
+ // Second time.
+ result = mLooper->removeFd(pipe.receiveFd);
+
+ EXPECT_EQ(0, result)
+ << "removeFd should return 0 second time because FD was no longer registered";
+}
+
+TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
+ Pipe pipe;
+ StubCallbackHandler handler1(true);
+ StubCallbackHandler handler2(true);
+
+ handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT);
+ handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it
+ pipe.writeSignal(); // would cause FD to be considered signalled
+
+ StopWatch stopWatch("pollOnce");
+ int result = mLooper->pollOnce(100);
+ int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+ ASSERT_EQ(OK, pipe.readSignal())
+ << "signal should actually have been written";
+ EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+ << "elapsed time should approx. zero because FD was already signalled";
+ EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+ << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled";
+ EXPECT_EQ(0, handler1.callbackCount)
+ << "original handler callback should not be invoked because it was replaced";
+ EXPECT_EQ(1, handler2.callbackCount)
+ << "replacement handler callback should be invoked";
+}
+
+
+} // namespace android
diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp
new file mode 100644
index 0000000..46b30c2
--- /dev/null
+++ b/libs/utils/tests/ObbFile_test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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 "ObbFile_test"
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+#define TEST_FILENAME "/test.obb"
+
+class ObbFileTest : public testing::Test {
+protected:
+ sp<ObbFile> mObbFile;
+ char* mExternalStorage;
+ char* mFileName;
+
+ virtual void SetUp() {
+ mObbFile = new ObbFile();
+ mExternalStorage = getenv("EXTERNAL_STORAGE");
+
+ const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1;
+ mFileName = new char[totalLen];
+ snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME);
+
+ int fd = ::open(mFileName, O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ FAIL() << "Couldn't create " << mFileName << " for tests";
+ }
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(ObbFileTest, ReadFailure) {
+ EXPECT_FALSE(mObbFile->readFrom(-1))
+ << "No failure on invalid file descriptor";
+}
+
+TEST_F(ObbFileTest, WriteThenRead) {
+ const char* packageName = "com.example.obbfile";
+ const int32_t versionNum = 1;
+
+ mObbFile->setPackageName(String8(packageName));
+ mObbFile->setVersion(versionNum);
+#define SALT_SIZE 8
+ unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5};
+ EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE))
+ << "Salt should be successfully set";
+
+ EXPECT_TRUE(mObbFile->writeTo(mFileName))
+ << "couldn't write to fake .obb file";
+
+ mObbFile = new ObbFile();
+
+ EXPECT_TRUE(mObbFile->readFrom(mFileName))
+ << "couldn't read from fake .obb file";
+
+ EXPECT_EQ(versionNum, mObbFile->getVersion())
+ << "version didn't come out the same as it went in";
+ const char* currentPackageName = mObbFile->getPackageName().string();
+ EXPECT_STREQ(packageName, currentPackageName)
+ << "package name didn't come out the same as it went in";
+
+ size_t saltLen;
+ const unsigned char* newSalt = mObbFile->getSalt(&saltLen);
+
+ EXPECT_EQ(sizeof(salt), saltLen)
+ << "salt sizes were not the same";
+
+ for (int i = 0; i < sizeof(salt); i++) {
+ EXPECT_EQ(salt[i], newSalt[i])
+ << "salt character " << i << " should be equal";
+ }
+ EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0)
+ << "salts should be the same";
+}
+
+}
diff --git a/libs/utils/tests/String8_test.cpp b/libs/utils/tests/String8_test.cpp
new file mode 100644
index 0000000..c42c68d
--- /dev/null
+++ b/libs/utils/tests/String8_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 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 "String8_test"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class String8Test : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(String8Test, Cstr) {
+ String8 tmp("Hello, world!");
+
+ EXPECT_STREQ(tmp.string(), "Hello, world!");
+}
+
+TEST_F(String8Test, OperatorPlus) {
+ String8 src1("Hello, ");
+
+ // Test adding String8 + const char*
+ const char* ccsrc2 = "world!";
+ String8 dst1 = src1 + ccsrc2;
+ EXPECT_STREQ(dst1.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ccsrc2, "world!");
+
+ // Test adding String8 + String8
+ String8 ssrc2("world!");
+ String8 dst2 = src1 + ssrc2;
+ EXPECT_STREQ(dst2.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ssrc2.string(), "world!");
+}
+
+TEST_F(String8Test, OperatorPlusEquals) {
+ String8 src1("My voice");
+
+ // Testing String8 += String8
+ String8 src2(" is my passport.");
+ src1 += src2;
+ EXPECT_STREQ(src1.string(), "My voice is my passport.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+
+ // Adding const char* to the previous string.
+ const char* src3 = " Verify me.";
+ src1 += src3;
+ EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src3, " Verify me.");
+}
+
+}
diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h
new file mode 100644
index 0000000..d8e985e
--- /dev/null
+++ b/libs/utils/tests/TestHelpers.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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 TESTHELPERS_H
+#define TESTHELPERS_H
+
+#include <utils/threads.h>
+
+namespace android {
+
+class Pipe {
+public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
+
+class DelayedTask : public Thread {
+ int mDelayMillis;
+
+public:
+ DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
+
+protected:
+ virtual ~DelayedTask() { }
+
+ virtual void doTask() = 0;
+
+ virtual bool threadLoop() {
+ usleep(mDelayMillis * 1000);
+ doTask();
+ return false;
+ }
+};
+
+} // namespace android
+
+#endif // TESTHELPERS_H